diff --git a/hudson-sflphone-script.sh b/hudson-sflphone-script.sh index 3f64d9334de143b27997c66270c91d7ebfdc93c9..412ea00cbd1cddf424f74096c1ad0616c8a52c24 100755 --- a/hudson-sflphone-script.sh +++ b/hudson-sflphone-script.sh @@ -16,6 +16,7 @@ pushd libs/pjproject make && make dep popd ./configure --prefix=/usr +make clean make make doc popd @@ -31,6 +32,7 @@ popd pushd sflphone-client-gnome ./autogen.sh ./configure --prefix=/usr +make clean make popd diff --git a/sflphone-client-gnome/src/actions.c b/sflphone-client-gnome/src/actions.c index a787748a8132021f57d39209a0230bcd2adb4b13..d6c73039998f8fc6693fc6cc6bbfd45af0baace3 100644 --- a/sflphone-client-gnome/src/actions.c +++ b/sflphone-client-gnome/src/actions.c @@ -51,7 +51,8 @@ #include <sys/ioctl.h> #include <linux/if.h> - +#include <widget/imwidget.h> +#include <imwindow.h> GHashTable * ip2ip_profile=NULL; @@ -199,8 +200,11 @@ sflphone_hung_up (callable_obj_t * c) call_remove_all_errors (c); update_actions(); - /* Update the IM interface */ - im_widget_update_state (c->_im_widget, FALSE); + // test wether the widget contain text, if not remove it + if ( (im_window_get_nb_tabs() > 1) && c->_im_widget && ! (IM_WIDGET (c->_im_widget)->containText)) + im_window_remove_tab (c->_im_widget); + else + im_widget_update_state (IM_WIDGET (c->_im_widget), FALSE); #if GTK_CHECK_VERSION(2,10,0) status_tray_icon_blink (FALSE); @@ -380,7 +384,13 @@ sflphone_hang_up() call_remove_all_errors (selectedCall); selectedCall->_state = CALL_STATE_DIALING; set_timestamp (&selectedCall->_time_stop); - im_widget_update_state (selectedCall->_im_widget, FALSE); + + //if ( (im_window_get_nb_tabs() > 1) && selectedCall->_im_widget && + // ! (IM_WIDGET (selectedCall->_im_widget)->containText)) + // im_window_remove_tab (selectedCall->_im_widget); + //else + im_widget_update_state (IM_WIDGET (selectedCall->_im_widget), FALSE); + break; case CALL_STATE_FAILURE: dbus_hang_up (selectedCall); @@ -404,6 +414,7 @@ sflphone_hang_up() break; } } else if (selectedConf) { + im_widget_update_state (IM_WIDGET (selectedConf->_im_widget), FALSE); dbus_hang_up_conference (selectedConf); } @@ -436,10 +447,20 @@ sflphone_pick_up() switch (selectedCall->_state) { case CALL_STATE_DIALING: sflphone_place_call (selectedCall); + + // if instant messaging window is visible, create new tab (deleted automatically if not used) + if (im_window_is_visible()) + im_widget_display ( (IMWidget **) (&selectedCall->_im_widget), NULL, selectedCall->_callID, NULL); + break; case CALL_STATE_INCOMING: selectedCall->_history_state = INCOMING; calltree_update_call (history, selectedCall, NULL); + + // if instant messaging window is visible, create new tab (deleted automatically if not used) + if (im_window_is_visible()) + im_widget_display ( (IMWidget **) (&selectedCall->_im_widget), NULL, selectedCall->_callID, NULL); + dbus_accept (selectedCall); DEBUG ("from sflphone_pick_up : "); stop_notification(); @@ -972,6 +993,10 @@ sflphone_detach_participant (const gchar* callID) selectedCall->_confID = NULL; } + // Instant messaging widget should have been deactivated during the conference + if (selectedCall->_im_widget) + im_widget_update_state (IM_WIDGET (selectedCall->_im_widget), TRUE); + calltree_remove_call (current_calls, selectedCall, NULL); calltree_add_call (current_calls, selectedCall, NULL); dbus_detach_participant (selectedCall->_callID); @@ -984,6 +1009,10 @@ sflphone_detach_participant (const gchar* callID) selectedCall->_confID = NULL; } + // Instant messagin widget should have been deactivated during the conference + if (selectedCall->_im_widget) + im_widget_update_state (IM_WIDGET (selectedCall->_im_widget), TRUE); + calltree_remove_call (current_calls, selectedCall, NULL); calltree_add_call (current_calls, selectedCall, NULL); dbus_detach_participant (callID); diff --git a/sflphone-client-gnome/src/callable_obj.c b/sflphone-client-gnome/src/callable_obj.c index f3cf8e9efee6b219d00081b8d46bc4474cb4162d..189c891ef8fbdd8c7a3be6f030721e627f5c8d8f 100644 --- a/sflphone-client-gnome/src/callable_obj.c +++ b/sflphone-client-gnome/src/callable_obj.c @@ -537,8 +537,6 @@ gchar* get_peer_information (callable_obj_t *c) { - gchar *res; - if (g_strcasecmp (c->_peer_name,"") == 0) return g_strdup (c->_peer_number); else diff --git a/sflphone-client-gnome/src/codeclist.c b/sflphone-client-gnome/src/codeclist.c index 284bc21d8168c45719217169377f0558eacc7dd2..d6fcadb3c910296f2e32b715a04c9c6598afa6ee 100644 --- a/sflphone-client-gnome/src/codeclist.c +++ b/sflphone-client-gnome/src/codeclist.c @@ -105,7 +105,6 @@ void codec_capabilities_load (void) void account_create_codec_list (account_t **acc) { - gchar **order = NULL; GQueue *_codecs; _codecs = (*acc)->codecs; diff --git a/sflphone-client-gnome/src/conference_obj.h b/sflphone-client-gnome/src/conference_obj.h index edec21a7880ae38ad207da00753756949ff0c654..56b731f7c8489ec94ce56dc1a86e3a99eece6c7c 100644 --- a/sflphone-client-gnome/src/conference_obj.h +++ b/sflphone-client-gnome/src/conference_obj.h @@ -56,10 +56,11 @@ typedef enum { typedef struct { conference_state_t _state; // The state of the call - gchar* _confID; // The call ID + gchar *_confID; // The call ID gboolean _conference_secured; // the security state of the conference gboolean _conf_srtp_enabled; // security required for this conference - GSList* participant_list; // participant list for this + GSList *participant_list; // participant list for this + GtkWidget *_im_widget; // associated instant messaging widget } conference_obj_t; diff --git a/sflphone-client-gnome/src/config/accountconfigdialog.c b/sflphone-client-gnome/src/config/accountconfigdialog.c index cb718fb473500e5948af916154f1cd0b95824779..9ab3de9e4f5a65df5d254e24df00564f398db074 100644 --- a/sflphone-client-gnome/src/config/accountconfigdialog.c +++ b/sflphone-client-gnome/src/config/accountconfigdialog.c @@ -31,6 +31,8 @@ * as that of the covered work. */ + + #include <actions.h> #include <mainwindow.h> #include <accountlist.h> @@ -74,7 +76,7 @@ GtkWidget * entryMailbox; GtkWidget * entryUseragent; GtkWidget * entryResolveNameOnlyOnce; GtkWidget * expireSpinBox; -GtkListStore * credentialStore; +GtkListStore * credentialStore = NULL; GtkWidget * deleteCredButton; GtkWidget * treeViewCredential; // GtkWidget * scrolledWindowCredential; @@ -259,10 +261,15 @@ static GPtrArray* getNewCredential (GHashTable * properties) static void update_credential_cb (GtkWidget *widget, gpointer data UNUSED) { GtkTreeIter iter; - gtk_tree_model_get_iter_from_string ( (GtkTreeModel *) credentialStore, &iter, "0"); - gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "column")); - // g_print ("set password to %s\n", (gchar *) gtk_entry_get_text(GTK_ENTRY(widget))); - gtk_list_store_set (GTK_LIST_STORE (credentialStore), &iter, column, (gchar *) gtk_entry_get_text (GTK_ENTRY (widget)), -1); + + if (credentialStore) { + if (gtk_tree_model_get_iter_from_string ( (GtkTreeModel *) credentialStore, &iter, "0")) { + gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "column")); + // g_print ("set password to %s\n", (gchar *) gtk_entry_get_text(GTK_ENTRY(widget))); + gtk_list_store_set (GTK_LIST_STORE (credentialStore), &iter, column, (gchar *) gtk_entry_get_text (GTK_ENTRY (widget)), -1); + } + } + } static GtkWidget* create_basic_tab (account_t **a) @@ -1474,8 +1481,10 @@ void show_account_window (account_t * a) } if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (overrtp))) { + DEBUG ("Config: Set dtmf over rtp"); g_hash_table_replace (currentAccount->properties, g_strdup (ACCOUNT_DTMF_TYPE), g_strdup (OVERRTP)); } else { + DEBUG ("Config: Set dtmf over sip"); g_hash_table_replace (currentAccount->properties, g_strdup (ACCOUNT_DTMF_TYPE), g_strdup (SIPINFO)); } diff --git a/sflphone-client-gnome/src/config/audioconf.c b/sflphone-client-gnome/src/config/audioconf.c index 6f767ea32ff912fb6e0f4bc0777307f768c4fdbc..7413c6047954919818591109d5e58f40ec84ee18 100644 --- a/sflphone-client-gnome/src/config/audioconf.c +++ b/sflphone-client-gnome/src/config/audioconf.c @@ -28,6 +28,7 @@ * as that of the covered work. */ + #include <audioconf.h> #include <utils.h> #include <string.h> @@ -752,28 +753,6 @@ select_audio_manager (void) } -void -active_echo_cancel (void) -{ - - gchar* state; - gchar* newstate; - - DEBUG ("Audio: Active echo cancel clicked"); - state = dbus_get_echo_cancel_state(); - - DEBUG ("Audio: Get echo cancel state %s", state); - - if (strcmp (state, "enabled") == 0) - newstate = "disabled"; - else - newstate = "enabled"; - - dbus_set_echo_cancel_state (newstate); - -} - - void active_noise_suppress (void) { @@ -781,10 +760,10 @@ active_noise_suppress (void) gchar *state; gchar *newstate; - DEBUG ("Audio: Active noise suppress clicked"); + DEBUG ("Audio: Active noise suppression clicked"); state = dbus_get_noise_suppress_state(); - DEBUG ("Audio: Get echo cancel state %s", state); + DEBUG ("Audio: Get noise suppression cancel state %s", state); if (strcmp (state, "enabled") == 0) newstate = "disabled"; @@ -919,9 +898,8 @@ GtkWidget* create_audio_configuration() GtkWidget *ret; // Sub boxes GtkWidget *frame; - GtkWidget *enableEchoCancel; GtkWidget *enableNoiseReduction; - gboolean echocancelActive, noisesuppressActive; + gboolean noisesuppressActive; gchar *state; ret = gtk_vbox_new (FALSE, 10); @@ -988,19 +966,6 @@ GtkWidget* create_audio_configuration() gnome_main_section_new_with_table (_ ("Voice enhancement settings"), &frame, &table, 2, 1); gtk_box_pack_start (GTK_BOX (ret), frame, FALSE, FALSE, 0); - enableEchoCancel = gtk_check_button_new_with_mnemonic (_ ("_Echo Suppression")); - state = dbus_get_echo_cancel_state(); - echocancelActive = FALSE; - - if (strcmp (state, "enabled") == 0) - echocancelActive = TRUE; - else - echocancelActive = FALSE; - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enableEchoCancel), echocancelActive); - g_signal_connect (G_OBJECT (enableEchoCancel), "clicked", active_echo_cancel, NULL); - gtk_table_attach (GTK_TABLE (table), enableEchoCancel, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - enableNoiseReduction = gtk_check_button_new_with_mnemonic (_ ("_Noise Reduction")); state = dbus_get_noise_suppress_state(); noisesuppressActive = FALSE; diff --git a/sflphone-client-gnome/src/config/preferencesdialog.c b/sflphone-client-gnome/src/config/preferencesdialog.c index 03a93e2ee37ec184c9c5ef59e9488bf8b67ca3a2..75bab7c8b91cccc9bad1ac524086c982ccf592f0 100644 --- a/sflphone-client-gnome/src/config/preferencesdialog.c +++ b/sflphone-client-gnome/src/config/preferencesdialog.c @@ -80,10 +80,13 @@ typedef struct { gint page_number; } browser_t; - +// history preference parameters static int history_limit; static gboolean history_enabled = TRUE; +// instant messaging preference parameters +static gboolean instant_messaging_enabled = TRUE; + static void start_hidden (void) { @@ -124,6 +127,14 @@ history_enabled_cb (GtkWidget *widget) eel_gconf_set_integer (HISTORY_ENABLED, !eel_gconf_get_integer (HISTORY_ENABLED)); } +static void +instant_messaging_enabled_cb (GtkWidget *widget) +{ + instant_messaging_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + eel_gconf_set_integer (INSTANT_MESSAGING_ENABLED, !eel_gconf_get_integer (INSTANT_MESSAGING_ENABLED)); +} + void clean_history (void) { @@ -160,6 +171,9 @@ create_general_settings () // Load history configuration history_load_configuration (); + // Load instant messaging configuration + instant_messaging_load_configuration(); + // Main widget ret = gtk_vbox_new (FALSE, 10); gtk_container_set_border_width (GTK_CONTAINER (ret), 10); @@ -248,6 +262,18 @@ create_general_settings () gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 5); + // INSTANT MESSAGING CONFIGURATION + gnome_main_section_new_with_table (_ ("Instant Messaging"), &frame, &table, 1, 1); + gtk_box_pack_start (GTK_BOX (ret), frame, FALSE, FALSE, 0); + + checkBoxWidget = gtk_check_button_new_with_mnemonic ( + _ ("Enable instant messaging")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkBoxWidget), + instant_messaging_enabled); + g_signal_connect (G_OBJECT (checkBoxWidget) , "clicked" , G_CALLBACK (instant_messaging_enabled_cb) , NULL); + gtk_table_attach (GTK_TABLE (table), checkBoxWidget, 0, 1, 0, 1, GTK_EXPAND + | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 5); + gtk_widget_show_all (ret); return ret; @@ -273,6 +299,14 @@ history_load_configuration () { history_limit = dbus_get_history_limit (); history_enabled = eel_gconf_get_integer (HISTORY_ENABLED); + +} + +void +instant_messaging_load_configuration () +{ + instant_messaging_enabled = eel_gconf_get_integer (INSTANT_MESSAGING_ENABLED); + } @@ -402,7 +436,7 @@ GtkTreeModel* createModel() { browser_t browser_entries[5] = { - {_ ("General"), "start-here", 0}, + {_ ("General"), "preferences-system", 0}, {_ ("Audio"), "multimedia-volume-control", 1}, {_ ("Address Book"), "address-book-new", 2}, {_ ("Hooks"), "gnome-globe", 3}, diff --git a/sflphone-client-gnome/src/config/preferencesdialog.h b/sflphone-client-gnome/src/config/preferencesdialog.h index c4a2a3f0be3036909f332490f96ab81afb8df3ed..c681e58be3d1fe056f1efcd969f6d6a72f075441 100644 --- a/sflphone-client-gnome/src/config/preferencesdialog.h +++ b/sflphone-client-gnome/src/config/preferencesdialog.h @@ -121,6 +121,8 @@ void save_configuration_parameters (void); void history_load_configuration (void); +void instant_messaging_load_configuration (void); + GtkTreeModel* createModel(); #endif diff --git a/sflphone-client-gnome/src/contacts/addressbook/eds.c b/sflphone-client-gnome/src/contacts/addressbook/eds.c index 0678e473abd111b78fbc133e69014206230eb6f6..2046fa4cbb918ed487b160df6a39ffb7203a23ec 100644 --- a/sflphone-client-gnome/src/contacts/addressbook/eds.c +++ b/sflphone-client-gnome/src/contacts/addressbook/eds.c @@ -548,7 +548,8 @@ fill_books_data () book_data->name = g_strdup (e_source_peek_name (source)); book_data->uid = g_strdup (e_source_peek_uid (source)); - gchar *prop = e_source_get_property (source, "default"); + const gchar *property_name = "default"; + const gchar *prop = e_source_get_property (source, property_name); if (prop) if (strcmp (prop, "true") == 0) diff --git a/sflphone-client-gnome/src/contacts/calltree.c b/sflphone-client-gnome/src/contacts/calltree.c index d6e4a0341fde7142d8cb9298fd5d96fcff7b69cd..fc56c02f7fbd6ccd4a9474880a896b89310d833c 100644 --- a/sflphone-client-gnome/src/contacts/calltree.c +++ b/sflphone-client-gnome/src/contacts/calltree.c @@ -40,6 +40,7 @@ #include <history.h> #include "uimanager.h" #include "actions.h" +#include "../imwindow.h" GtkWidget *sw; GtkCellRenderer *rend; @@ -130,6 +131,10 @@ call_selected_cb (GtkTreeSelection *sel, void* data UNUSED) selected_path = string_path; selected_call = NULL; + if (selected_conf->_im_widget) { + // show the coresponding widget + im_window_show_tab (selected_conf->_im_widget); + } } DEBUG ("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d", @@ -152,6 +157,11 @@ call_selected_cb (GtkTreeSelection *sel, void* data UNUSED) selected_call_id = selected_call->_callID; selected_path = string_path; selected_conf = NULL; + + if (selected_call->_im_widget) { + // show the coresponding widget + im_window_show_tab (selected_call->_im_widget); + } } DEBUG ("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d", diff --git a/sflphone-client-gnome/src/contacts/searchbar.c b/sflphone-client-gnome/src/contacts/searchbar.c index db416d659c1ebec6a4e7f09033ec88c660cf9397..5363ad496ec9cd7f84fce42e296949fee0dfbd55 100644 --- a/sflphone-client-gnome/src/contacts/searchbar.c +++ b/sflphone-client-gnome/src/contacts/searchbar.c @@ -39,7 +39,7 @@ GtkWidget * searchbox; GtkWidget * addressbookentry; GtkWidget * cbox; -GtkListStore * liststore; +GtkListStore * liststore = NULL; gint cboxSignalId; @@ -109,7 +109,7 @@ void update_searchbar_addressbook_list() = book_list_iterator->next) { book_data = (book_data_t *) book_list_iterator->data; - if (book_data->active) { + if (book_data && book_data->active && activeText) { gtk_list_store_append (liststore, &iter); gtk_list_store_set (liststore, &iter, 0, book_data->name, -1); diff --git a/sflphone-client-gnome/src/dbus/configurationmanager-introspec.xml b/sflphone-client-gnome/src/dbus/configurationmanager-introspec.xml index c2818831f9e18520fe3bbfbe9c0982bd6a2133a2..f051f3b44bfb685a4408e18bf578024f49831f75 100755 --- a/sflphone-client-gnome/src/dbus/configurationmanager-introspec.xml +++ b/sflphone-client-gnome/src/dbus/configurationmanager-introspec.xml @@ -545,22 +545,6 @@ </arg> </method> - <method name="getEchoCancelState" tp:name-for-bindings="getEchoCancelState"> - <tp:docstring> - </tp:docstring> - <arg type="s" name="state" direction="out"> - <tp:docstring> - </tp:docstring> - </arg> - </method> - - <method name="setEchoCancelState" tp:name-for-bindings="setEchoCancelState"> - <tp:docstring> - </tp:docstring> - <arg type="s" name="state" direction="in"> - </arg> - </method> - <method name="getNoiseSuppressState" tp:name-for-bindings="getNoiseSuppressState"> <tp:docstring> </tp:docstring> diff --git a/sflphone-client-gnome/src/dbus/dbus.c b/sflphone-client-gnome/src/dbus/dbus.c index 1b5beb16a7cf2c31325ded46ba8661cfa4bb1f10..69354e26d7f606640728373521bdd144bad8d2dc 100644 --- a/sflphone-client-gnome/src/dbus/dbus.c +++ b/sflphone-client-gnome/src/dbus/dbus.c @@ -49,6 +49,8 @@ #include <widget/imwidget.h> +#include <eel-gconf-extensions.h> + #define DEFAULT_DBUS_TIMEOUT 30000 DBusGConnection * connection; @@ -122,19 +124,38 @@ voice_mail_cb (DBusGProxy *proxy UNUSED, const gchar* accountID, const guint nb, static void incoming_message_cb (DBusGProxy *proxy UNUSED, const gchar* callID UNUSED, const gchar *from, const gchar* msg, void * foo UNUSED) { - DEBUG ("Message %s!",msg); + DEBUG ("Message \"%s\" from %s!", msg, from); - // Get the call information. Does this call exist? - callable_obj_t * c = calllist_get (current_calls, callID); + callable_obj_t *call = NULL; + conference_obj_t *conf = NULL; + + // do not display message if instant messaging is disabled + gboolean instant_messaging_enabled = TRUE; + + if (eel_gconf_key_exists (INSTANT_MESSAGING_ENABLED)) + instant_messaging_enabled = eel_gconf_get_integer (INSTANT_MESSAGING_ENABLED); + + if (!instant_messaging_enabled) + return; + + // Get the call information. (if this call exist) + call = calllist_get (current_calls, callID); + + // Get the conference information (if this conference exist) + conf = conferencelist_get (callID); /* First check if the call is valid */ - if (c) { + if (call) { /* Make the instant messaging main window pops, add messages only if the main window exist. - Elsewhere the message is displayed asynchronously*/ - if (im_widget_display (&c, msg)) - im_widget_add_message (c->_im_widget, get_peer_information (c), msg, 0); - + Elsewhere the message is displayed asynchronously*/ + if (im_widget_display ( (IMWidget **) (&call->_im_widget), msg, call->_callID, from)) + im_widget_add_message (IM_WIDGET (call->_im_widget), from, msg, 0); + } else if (conf) { + /* Make the instant messaging main window pops, add messages only if the main window exist. + Elsewhere the message is displayed asynchronously*/ + if (im_widget_display ( (IMWidget **) (&conf->_im_widget), msg, conf->_confID, from)) + im_widget_add_message (IM_WIDGET (conf->_im_widget), from, msg, 0); } else { ERROR ("Message received, but no recipient found"); } @@ -218,9 +239,13 @@ conference_changed_cb (DBusGProxy *proxy UNUSED, const gchar* confID, const gchar* state, void * foo UNUSED) { + gchar** part; + callable_obj_t *call; + gchar* call_id; + // sflphone_display_transfer_status("Transfer successfull"); conference_obj_t* changed_conf = conferencelist_get (confID); - gchar** participants; + gchar** old_participants, new_participants; DEBUG ("conference new state %s\n", state); @@ -239,10 +264,32 @@ conference_changed_cb (DBusGProxy *proxy UNUSED, const gchar* confID, DEBUG ("Error: conference state not recognized"); } - participants = (gchar**) dbus_get_participant_list (changed_conf->_confID); + // reactivate instant messaging window for these calls + old_participants = (gchar**) changed_conf->participant_list; + + for (part = old_participants; *part; part++) { + call_id = (gchar*) (*part); + call = calllist_get (current_calls, call_id); + + if (call && call->_im_widget) + im_widget_update_state (IM_WIDGET (call->_im_widget), TRUE); + } + + new_participants = (gchar **) dbus_get_participant_list (changed_conf->_confID); // update conferece participants - conference_participant_list_update (participants, changed_conf); + conference_participant_list_update (new_participants, changed_conf); + + // deactivate instant messaging window for new participants + new_participants = (gchar**) changed_conf->participant_list; + + for (part = new_participants; *part; part++) { + call_id = (gchar*) (*part); + call = calllist_get (current_calls, call_id); + + if (call && call->_im_widget) + im_widget_update_state (IM_WIDGET (call->_im_widget), FALSE); + } // add new conference to calltree calltree_add_conference (current_calls, changed_conf); @@ -271,6 +318,11 @@ conference_created_cb (DBusGProxy *proxy UNUSED, const gchar* confID, void * foo for (part = participants; *part; part++) { call_id = (gchar*) (*part); call = calllist_get (current_calls, call_id); + + // if a text widget is already created, disable it, use conference widget instead + if (call->_im_widget) + im_widget_update_state (IM_WIDGET (call->_im_widget), FALSE); + call->_confID = g_strdup (confID); } @@ -289,6 +341,11 @@ conference_removed_cb (DBusGProxy *proxy UNUSED, const gchar* confID, void * foo GSList *participant = c->participant_list; callable_obj_t *call; + // deactivate instant messaging window for this conference + if (c->_im_widget) + im_widget_update_state (IM_WIDGET (c->_im_widget), FALSE); + + // remove all participant for this conference while (participant) { call = calllist_get (current_calls, (const gchar *) (participant->data)); @@ -300,6 +357,10 @@ conference_removed_cb (DBusGProxy *proxy UNUSED, const gchar* confID, void * foo g_free (call->_confID); call->_confID = NULL; } + + // if an instant messaging was previously disabled, enabled it + if (call->_im_widget) + im_widget_update_state (IM_WIDGET (call->_im_widget), TRUE); } participant = conference_next_participant (participant); @@ -1404,40 +1465,6 @@ dbus_get_current_audio_output_plugin() return plugin; } -/** - * Get echo canceller state - */ -gchar* -dbus_get_echo_cancel_state() -{ - gchar* state = ""; - GError* error = NULL; - org_sflphone_SFLphone_ConfigurationManager_get_echo_cancel_state (configurationManagerProxy, &state, &error); - - if (error) { - ERROR ("DBus: Failed to call get_echo_cancel_state() on ConfigurationManager: %s", error->message); - g_error_free (error); - } - - return state; -} - -/** - * Set echo canceller state - */ -void -dbus_set_echo_cancel_state (gchar* state) -{ - GError* error = NULL; - org_sflphone_SFLphone_ConfigurationManager_set_echo_cancel_state ( - configurationManagerProxy, state, &error); - - if (error) { - ERROR ("Failed to call set_echo_cancel_state() on ConfigurationManager: %s", error->message); - g_error_free (error); - } -} - /** * Get noise reduction state @@ -1458,7 +1485,7 @@ dbus_get_noise_suppress_state() } /** - * Set echo canceller state + * Set noise reduction state */ void dbus_set_noise_suppress_state (gchar* state) @@ -1958,10 +1985,10 @@ dbus_get_conference_list (void) } gchar** -dbus_get_participant_list (const char *confID) +dbus_get_participant_list (const gchar *confID) { GError *error = NULL; - gchar **list = NULL; + char **list = NULL; DEBUG ("DBUS: Get conference %s participant list", confID); diff --git a/sflphone-client-gnome/src/dbus/dbus.h b/sflphone-client-gnome/src/dbus/dbus.h index 99dce013df8db99d36ef8a262c3c72fded03a5c5..38f1bfc47aef81420bd30da177d41b7042bb6469 100644 --- a/sflphone-client-gnome/src/dbus/dbus.h +++ b/sflphone-client-gnome/src/dbus/dbus.h @@ -250,6 +250,13 @@ gchar* dbus_get_current_codec_name (const callable_obj_t * c); */ gchar** dbus_get_audio_plugin_list(); + +/** + * ConfigurationManager - Select an input audio plugin + * @param audioPlugin The string description of the plugin + */ +void dbus_set_audio_plugin (gchar* audioPlugin); + /** * ConfigurationManager - Select an input audio plugin * @param audioPlugin The string description of the plugin @@ -308,19 +315,6 @@ int dbus_get_audio_device_index (const gchar* name); */ gchar* dbus_get_current_audio_output_plugin(); - -/** - * ConfigurationManager - Get the current state of echo canceller - * @return gchar* The state (enabled/disabled) - */ -gchar *dbus_get_echo_cancel_state (void); - -/** - * ConfigurationManager - Set the crrent state of echo canceller - * @param gchar* The state (enabled/disabled) - */ -void dbus_set_echo_cancel_state (gchar *state); - /** * ConfigurationManager - Get the current noise suppressor state * @return gchar* The state (enabled/disabled) @@ -435,11 +429,30 @@ void dbus_set_sip_address (const gchar* address); gint dbus_get_sip_address (void); + +/** + * Add a participant (callID) to this conference (confID) + */ void dbus_add_participant (const gchar* callID, const gchar* confID); +/** + * Return a list of participant for this conference (confID) + */ +gchar** dbus_get_participant_list (const gchar *confID); + +/** + * Toggle recording for this instance, may be call or conference + */ void dbus_set_record (const gchar * id); +/** + * Set the path where the recorded audio files will be stored + */ void dbus_set_record_path (const gchar *path); + +/** + * Get the path where the recorded audio files are stored + */ gchar* dbus_get_record_path (void); /** @@ -539,9 +552,8 @@ void dbus_set_confirm_go_clear (const callable_obj_t * c); */ gchar** dbus_get_supported_tls_method(); -gchar** dbus_get_participant_list (const char * confID); - GHashTable* dbus_get_shortcuts (void); + void dbus_set_shortcuts (GHashTable * shortcuts); void dbus_set_audio_ringtone_device (const int index); diff --git a/sflphone-client-gnome/src/imwindow.c b/sflphone-client-gnome/src/imwindow.c index 881e092e8800560e39cbec945d52351bdbf91d14..e9bb916c1f3938d08aa050b26482c7f3132e4940 100644 --- a/sflphone-client-gnome/src/imwindow.c +++ b/sflphone-client-gnome/src/imwindow.c @@ -37,12 +37,13 @@ #include <imwindow.h> #include <contacts/calltab.h> +#include <contacts/calltab.h> /** Local variables */ GtkWidget *im_window = NULL; GtkWidget *im_notebook = NULL; -static gboolean window_configure_cb (GtkWidget *win, GdkEventConfigure *event) +static gboolean window_configure_cb (GtkWidget *wini UNUSED, GdkEventConfigure *event) { int pos_x, pos_y; @@ -68,7 +69,7 @@ on_delete (GtkWidget * widget UNUSED, gpointer data UNUSED) } static void -on_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, gpointer userdata) +on_switch_page (GtkNotebook *notebook, GtkNotebookPage *page UNUSED, guint page_num, gpointer userdata UNUSED) { guint index = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)); @@ -85,9 +86,6 @@ static void im_window_init() { const char *window_title = "SFLphone IM Client"; - gchar *path; - GError *error = NULL; - gboolean ret; int width, height, position_x, position_y; // Get configuration stored in gconf @@ -140,10 +138,27 @@ im_window_get() return im_window; } + +gboolean +im_window_is_active () +{ + + if (!im_window) + return FALSE; + else + return gtk_window_is_active (im_window); +} + +gboolean +im_window_is_visible () +{ + return gtk_widget_get_visible (im_window); +} + void im_window_show () { - gtk_widget_show (im_window_get ()); + gtk_window_present (GTK_WINDOW (im_window_get ())); } void @@ -156,11 +171,20 @@ im_window_add (GtkWidget *widget) /* Show it all */ gtk_widget_show_all (im_window); } else - error ("Could not create the main instant messaging window"); + ERROR ("Could not create the main instant messaging window"); +} + +gint +im_window_get_nb_tabs() +{ + if (im_notebook) + return gtk_notebook_get_n_pages (im_notebook); + else + return 0; } static void -close_tab_cb (GtkButton *button, gpointer userdata) +close_tab_cb (GtkButton *button UNUSED, gpointer userdata) { /* We want here to close the current tab */ im_window_remove_tab (GTK_WIDGET (userdata)); @@ -177,10 +201,20 @@ im_window_add_tab (GtkWidget *widget) /* Fetch the call */ callable_obj_t *im_widget_call = calllist_get (current_calls, im->call_id); + conference_obj_t *im_widget_conf = conferencelist_get (im->call_id); /* A container to include the tab label and the close button */ GtkWidget *tab_Container = gtk_hbox_new (FALSE, 3); - GtkWidget *tab_Label = gtk_label_new (get_peer_information (im_widget_call)); + GtkWidget *tab_Label; + im->tab = tab_Container; + + if (im_widget_call) + tab_Label = gtk_label_new (get_peer_information (im_widget_call)); + else if (im_widget_conf) + tab_Label = gtk_label_new ("Conferencing"); + else + tab_Label = gtk_label_new (""); + GtkWidget *tab_CloseButton = gtk_button_new (); /* Pack it all */ @@ -218,6 +252,15 @@ im_window_hide_show_tabs () } +void +im_window_show_tab (GtkWidget *widget) +{ + int pageIndex = gtk_notebook_page_num (GTK_NOTEBOOK (im_notebook), widget); + + if (pageIndex != -1) + gtk_notebook_set_current_page (GTK_NOTEBOOK (im_notebook), pageIndex); +} + void im_window_remove_tab (GtkWidget *widget) { @@ -230,10 +273,14 @@ im_window_remove_tab (GtkWidget *widget) /* Need to do some memory clean up, so that we could re-open an Im widget for this call later. */ IMWidget *im = IM_WIDGET (widget); callable_obj_t *call = calllist_get (current_calls, im->call_id); + conference_obj_t *conf = conferencelist_get (im->call_id); if (call) call->_im_widget = NULL; + if (conf) + conf->_im_widget = NULL; + /* Decide whether or not displaying the tabs of the notebook */ im_window_hide_show_tabs (); } diff --git a/sflphone-client-gnome/src/imwindow.h b/sflphone-client-gnome/src/imwindow.h index 028efc677a74d280d057d9c7ec4aac3c80d63107..f0d1de2e0774a4da3eb11345c69952cd2084cc40 100644 --- a/sflphone-client-gnome/src/imwindow.h +++ b/sflphone-client-gnome/src/imwindow.h @@ -60,6 +60,21 @@ void im_window_remove_tab (GtkWidget *widget); void im_window_show (); +/** + * Return wether the instant messaging window have been created or not + */ +gboolean im_window_is_active (void); + +/** + * Return wether the instant messaging window is visible + */ +gboolean im_window_is_visible (void); + +/** + * Return the number of tabs already open in instant messaging window + */ +gint im_window_get_nb_tabs (void); + /*! @function @abstract Add a new tab in the notebook. Each tab is an IM Widget @param The IM widget @@ -71,4 +86,10 @@ void im_window_add_tab (GtkWidget *widget); */ void im_window_hide_show_tabs (); +/*! @function +@abstract Select the specified tab as current in instant messaging window +@param The tab to be set as current +*/ +void im_window_show_tab (GtkWidget *widget); + #endif diff --git a/sflphone-client-gnome/src/sflnotify.c b/sflphone-client-gnome/src/sflnotify.c index 76de2d319460f15a9e9f950d32e51467373ec805..b90586c280f17d010a8448c7c296057b120cfce8 100644 --- a/sflphone-client-gnome/src/sflnotify.c +++ b/sflphone-client-gnome/src/sflnotify.c @@ -72,7 +72,6 @@ void notify_incoming_message (const gchar *callID, const gchar *msg) { - gchar* text; gchar* title; title = g_markup_printf_escaped (_ ("%s says:"), callID); diff --git a/sflphone-client-gnome/src/sflphone_const.h b/sflphone-client-gnome/src/sflphone_const.h index 034a40bb44bbdccc3f750f41a4b67b9f984558a3..fb74d0219806158340bacdb909a109cbe5a9525f 100644 --- a/sflphone-client-gnome/src/sflphone_const.h +++ b/sflphone-client-gnome/src/sflphone_const.h @@ -194,5 +194,6 @@ #define START_HIDDEN CONF_PREFIX "/state/start_hidden" #define POPUP_ON_CALL CONF_PREFIX "/state/popup" #define HISTORY_ENABLED CONF_PREFIX "/state/history" +#define INSTANT_MESSAGING_ENABLED CONF_PREFIX "/state/instant_messaging" #endif diff --git a/sflphone-client-gnome/src/uimanager.c b/sflphone-client-gnome/src/uimanager.c index 6fa9319b6a2c9b4831ce802c0cc88f5a78822d81..d896db30dd2c3b638df1d7054ffa3349b15140c2 100644 --- a/sflphone-client-gnome/src/uimanager.c +++ b/sflphone-client-gnome/src/uimanager.c @@ -191,6 +191,11 @@ update_actions() callable_obj_t * selectedCall = calltab_get_selected_call (active_calltree); conference_obj_t * selectedConf = calltab_get_selected_conf (active_calltree); + gboolean instant_messaging_enabled = TRUE; + + if (eel_gconf_key_exists (INSTANT_MESSAGING_ENABLED)) + instant_messaging_enabled = eel_gconf_get_integer (INSTANT_MESSAGING_ENABLED); + if (selectedCall) { // update icon in systray show_status_hangup_icon(); @@ -215,11 +220,16 @@ update_actions() gtk_widget_set_sensitive (GTK_WIDGET (holdMenu), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (offHoldToolbar), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (newCallWidget), TRUE); + // Replace the hold button with the off-hold button - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), - 1); - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), - GTK_TOOL_ITEM (offHoldToolbar), 2); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), 1); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (offHoldToolbar), 2); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 3); + } + break; case CALL_STATE_RINGING: gtk_action_set_sensitive (GTK_ACTION (pickUpAction), TRUE); @@ -250,21 +260,21 @@ update_actions() gtk_widget_set_sensitive (GTK_WIDGET (holdToolbar), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (transferToolbar), TRUE); gtk_action_set_sensitive (GTK_ACTION (recordAction), TRUE); - gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (holdToolbar), - 2); - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), - GTK_TOOL_ITEM (transferToolbar), 3); - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), - 4); - gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), - 5); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (holdToolbar), 2); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (transferToolbar), 3); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), 4); gtk_signal_handler_block (GTK_OBJECT (transferToolbar), transfertButtonConnId); gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (transferToolbar), FALSE); gtk_signal_handler_unblock (transferToolbar, transfertButtonConnId); g_signal_handler_block (GTK_OBJECT (recordWidget), recordButtonConnId); gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (recordWidget), FALSE); g_signal_handler_unblock (GTK_OBJECT (recordWidget), recordButtonConnId); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 5); + } + break; case CALL_STATE_RECORD: @@ -284,6 +294,12 @@ update_actions() g_signal_handler_block (GTK_OBJECT (recordWidget), recordButtonConnId); gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (recordWidget), TRUE); g_signal_handler_unblock (GTK_OBJECT (recordWidget), recordButtonConnId); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 5); + } + break; case CALL_STATE_BUSY: case CALL_STATE_FAILURE: @@ -319,6 +335,12 @@ update_actions() gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), 1); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (holdToolbar), 2); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), 3); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 4); + } + break; case CONFERENCE_STATE_ACTIVE_DETACHED: @@ -328,6 +350,12 @@ update_actions() gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), 1); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (holdToolbar), 2); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), 3); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 4); + } + break; case CONFERENCE_STATE_RECORD: @@ -338,6 +366,12 @@ update_actions() gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), 1); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (holdToolbar), 2); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), 3); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 4); + } + break; case CONFERENCE_STATE_HOLD: @@ -347,6 +381,12 @@ update_actions() gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (hangUpWidget), 1); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (offHoldToolbar), 2); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (recordWidget), 3); + + if (instant_messaging_enabled) { + gtk_action_set_sensitive (GTK_ACTION (imAction), TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (imToolbar), 4); + } + break; default: @@ -534,12 +574,22 @@ call_hold (void* foo UNUSED) static void call_im (void* foo UNUSED) { - callable_obj_t * selectedCall = calltab_get_selected_call (current_calls); + callable_obj_t *selectedCall = calltab_get_selected_call (current_calls); + conference_obj_t *selectedConf = calltab_get_selected_conf(); - if (selectedCall) { - im_widget_display (&selectedCall, NULL); + if (calltab_get_selected_type (current_calls) == A_CALL) { + + if (selectedCall) { + im_widget_display ( (IMWidget **) (&selectedCall->_im_widget), NULL, selectedCall->_callID, NULL); + } else { + WARN ("Sorry. Instant messaging is not allowed outside a call\n"); + } } else { - warn ("Sorry. Instant messaging is not allowed outside a call\n"); + if (selectedConf) { + im_widget_display ( (IMWidget **) (&selectedConf->_im_widget), NULL, selectedConf->_confID, NULL); + } else { + WARN ("Sorry. Instant messaging is not allowed outside a call\n"); + } } } @@ -1191,18 +1241,28 @@ show_popup_menu (GtkWidget *my_widget, GdkEventButton *event) } if (im) { - menu_items = gtk_separator_menu_item_new(); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items); - gtk_widget_show (menu_items); - menu_items = gtk_image_menu_item_new_with_mnemonic (_ ("Send _message")); - image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_items), image); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items); - g_signal_connect (G_OBJECT (menu_items), "activate", - G_CALLBACK (call_im), - NULL); - gtk_widget_show (menu_items); + // do not display message if instant messaging is disabled + gboolean instant_messaging_enabled = TRUE; + + if (eel_gconf_key_exists (INSTANT_MESSAGING_ENABLED)) + instant_messaging_enabled = eel_gconf_get_integer (INSTANT_MESSAGING_ENABLED); + + if (instant_messaging_enabled) { + + menu_items = gtk_separator_menu_item_new(); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items); + gtk_widget_show (menu_items); + + menu_items = gtk_image_menu_item_new_with_mnemonic (_ ("Send _message")); + image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_items), image); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_items); + g_signal_connect (G_OBJECT (menu_items), "activate", + G_CALLBACK (call_im), + NULL); + gtk_widget_show (menu_items); + } } } else { diff --git a/sflphone-client-gnome/src/widget/imwidget.c b/sflphone-client-gnome/src/widget/imwidget.c index 494af52617b2bc20d33075106358506ac195347c..367a200b73a357bed69e63bed83bbb58ea1cf3db 100644 --- a/sflphone-client-gnome/src/widget/imwidget.c +++ b/sflphone-client-gnome/src/widget/imwidget.c @@ -27,34 +27,46 @@ * as that of the covered work. */ +#include <imwindow.h> #include "imwidget.h" #include <icons/icon_factory.h> #include <contacts/calltab.h> +#include <contacts/conferencelist.h> #include <JavaScriptCore/JavaScript.h> #include <gdk/gdkkeysyms.h> #define WEBKIT_DIR "file://" DATA_DIR "/webkit/" static void -on_frame_loading_done (GObject *gobject, GParamSpec *pspec, gpointer user_data) +on_frame_loading_done (GObject *gobject UNUSED, GParamSpec *pspec UNUSED, gpointer user_data) { IMWidget *im = IM_WIDGET (user_data); - callable_obj_t *c; + callable_obj_t *call; + conference_obj_t *conf; - if (im->first_message) { + if (im->first_message && im->first_message_from) { switch (webkit_web_frame_get_load_status (WEBKIT_WEB_FRAME (im->web_frame))) { case WEBKIT_LOAD_PROVISIONAL: case WEBKIT_LOAD_COMMITTED: break; case WEBKIT_LOAD_FINISHED: - c = calllist_get (current_calls, im->call_id); - im_widget_add_message (im, get_peer_information (c), im->first_message, 0); + call = calllist_get (current_calls, im->call_id); + conf = conferencelist_get (im->call_id); + + if (call) + im_widget_add_message (im, im->first_message_from, im->first_message, 0); + + if (conf) + im_widget_add_message (im, im->first_message_from, im->first_message, 0); + g_free (im->first_message); + g_free (im->first_message_from); im->first_message = NULL; + im->first_message_from = NULL; DEBUG ("JavaScrip loading frame finished"); break; case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT: - case WEBKIT_LOAD_FAILED: + // case WEBKIT_LOAD_FAILED: // only available in webkit-1.0-2 break; } } @@ -62,42 +74,24 @@ on_frame_loading_done (GObject *gobject, GParamSpec *pspec, gpointer user_data) } gchar * -escape_single_quotes (gchar *message) +escape_single_quotes (const gchar *message) { gchar **ptr_token; - gchar *string; + gchar *string = ""; DEBUG ("message: %s", message); - if (ptr_token = g_strsplit (message, "'", NULL)) { - string = *ptr_token; - ptr_token++; + if ( (ptr_token = g_strsplit (message, "'", 0))) { DEBUG ("SPLITTING"); - // string = g_strjoinv ("\\'", ptr_token); - - while (*ptr_token) { - DEBUG ("tokens: %s", *ptr_token); - - // if (g_strcmp0 (string[ (strlen (string)-1) ], "\\") == 0) { - // DEBUG ("already escaped last character %s", string[ (strlen (string)-1) ]); - // string = g_strdup_printf ("%s'%s", string, *ptr_token); - // } else - string = g_strdup_printf ("%s\\'%s", string, *ptr_token); - - ptr_token++; - } + string = g_strjoinv ("\\'", ptr_token); } - - DEBUG ("string: %s", string); - return string; } void im_widget_add_message (IMWidget *im, const gchar *from, const gchar *message, gint level) { - gboolean loadingDone = FALSE; if (im) { @@ -107,24 +101,30 @@ im_widget_add_message (IMWidget *im, const gchar *from, const gchar *message, gi /* Check for the message level */ gchar *css_class = (level == MESSAGE_LEVEL_ERROR) ? "error" : ""; + gchar *message_escaped = escape_single_quotes (message); + /* Prepare and execute the Javascript code */ - gchar *script = g_strdup_printf ("add_message('%s', '%s', '%s', '%s');", escape_single_quotes (message), from, css_class, msgtime); + gchar *script = g_strdup_printf ("add_message('%s', '%s', '%s', '%s');", message_escaped, from, css_class, msgtime); webkit_web_view_execute_script (WEBKIT_WEB_VIEW (im->web_view), script); + /* Mark it as used */ + im->containText = TRUE; + /* Cleanup */ g_free (script); + g_free (message_escaped); } } static gboolean web_view_nav_requested_cb ( - WebKitWebView *web_view, - WebKitWebFrame *frame, + WebKitWebView *web_view UNUSED, + WebKitWebFrame *frame UNUSED, WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action, + WebKitWebNavigationAction *navigation_action UNUSED, WebKitWebPolicyDecision *policy_decision, - gpointer user_data) + gpointer user_data UNUSED) { const gchar *uri = webkit_network_request_get_uri (request); @@ -134,7 +134,10 @@ web_view_nav_requested_cb ( } else { /* Running a system command to open the URL in the user's default browser */ gchar *cmd = g_strdup_printf ("x-www-browser %s", uri); - system (cmd); + + if (system (cmd) == -1) + ERROR ("Error executing command %s", cmd); + webkit_web_policy_decision_ignore (policy_decision); g_free (cmd); } @@ -143,21 +146,13 @@ web_view_nav_requested_cb ( } static gboolean -on_Textview_changed (GtkWidget *widget, GdkEventKey *event, gpointer user_data) +on_Textview_changed (GtkWidget *widget UNUSED, GdkEventKey *event, gpointer user_data) { GtkTextIter start, end; /* Get all the text in the buffer */ IMWidget *im = user_data; - callable_obj_t *im_widget_call = calllist_get (current_calls, im->call_id); - /* If the call has been hungup, it is not anymore in the current_calls calltab */ - if (!im_widget_call) { - /* So try the history tab */ - im_widget_call = calllist_get (history, im->call_id); - } - - /* Fetch the text entered in the GtkTextView */ GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (im->textarea)); /* Catch the keyboard events */ @@ -177,7 +172,7 @@ on_Textview_changed (GtkWidget *widget, GdkEventKey *event, gpointer user_data) im_widget_add_message (im, "Me", message, MESSAGE_LEVEL_NORMAL); /* Send the message to the peer */ - im_widget_send_message (im_widget_call, message); + im_widget_send_message (im->call_id, message); /* Empty the buffer */ gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer), &start, &end); @@ -205,31 +200,45 @@ im_widget_add_message_time () /* Get the time of the message. Format: HH:MM::SS */ strftime ( (char *) str, 100, "%R", (const struct tm *) ptr); - gchar *res = g_strdup (str); + gchar *res = g_strdup ( (gchar *) str); /* Return the new value */ return res; } void -im_widget_send_message (callable_obj_t *call, const gchar *message) +im_widget_send_message (gchar *id, const gchar *message) { + callable_obj_t *im_widget_call = calllist_get (current_calls, id); + conference_obj_t *im_widget_conf = conferencelist_get (id); + + /* If the call has been hungup, it is not anymore in the current_calls calltab */ + if (!im_widget_call) { + /* So try the history tab */ + im_widget_call = calllist_get (history, id); + } + + if (im_widget_conf) { + dbus_send_text_message (id, message); + } /* First check if the call is in CURRENT state, otherwise it could not be sent */ - if (call) { - if (call->_type == CALL && (call->_state == CALL_STATE_CURRENT || call->_state == CALL_STATE_HOLD)) { + else if (im_widget_call) { + if (im_widget_call->_type == CALL && (im_widget_call->_state == CALL_STATE_CURRENT || + im_widget_call->_state == CALL_STATE_HOLD || + im_widget_call->_state == CALL_STATE_RECORD)) { /* Ship the message through D-Bus */ - dbus_send_text_message (call->_callID, message); + dbus_send_text_message (id, message); } else { /* Display an error message */ - im_widget_add_message (IM_WIDGET (call->_im_widget), "sflphoned", "Oups, something went wrong! Unable to send text messages outside a call.", MESSAGE_LEVEL_ERROR); + im_widget_add_message (IM_WIDGET (im_widget_call->_im_widget), "sflphoned", "Oups, something went wrong! Unable to send text messages outside a call.", MESSAGE_LEVEL_ERROR); } } } static void -im_widget_class_init (IMWidgetClass *klass) +im_widget_class_init (IMWidgetClass *klass UNUSED) { } @@ -269,6 +278,8 @@ im_widget_init (IMWidget *im) im->js_global = JSContextGetGlobalObject (im->js_context); webkit_web_view_load_uri (WEBKIT_WEB_VIEW (im->web_view), "file://" DATA_DIR "/webkit/im/im.html"); + im->containText = FALSE; + g_signal_connect (G_OBJECT (im->web_frame), "notify", G_CALLBACK (on_frame_loading_done), im); } @@ -279,7 +290,7 @@ im_widget_new() } GtkWidget * -im_widget_new_with_first_message (gchar *message) +im_widget_new_with_first_message (const gchar *message UNUSED) { return GTK_WIDGET (g_object_new (IM_WIDGET_TYPE, NULL)); // return GTK_WIDGET (g_object_new (IM_WIDGET_TYPE, "first_message", message, NULL)); @@ -316,51 +327,58 @@ im_widget_get_type (void) } gboolean -im_widget_display (callable_obj_t **call, gchar *message) +im_widget_display (IMWidget **im, const gchar *message, const gchar *id, const gchar *from) { /* Work with a copy of the object */ - callable_obj_t *tmp = *call; + // callable_obj_t *tmp = *call; /* Use the widget for this specific call, if exists */ - if (tmp) { - IMWidget *im = IM_WIDGET (tmp->_im_widget); + // if (tmp) { + IMWidget *imwidget = *im;// = IM_WIDGET (tmp->_im_widget); - if (!im) { - DEBUG ("creating the im widget for this call\n"); + if (!imwidget) { + DEBUG ("creating the im widget for this call\n"); - /* Create the im object */ - if (message) - im = IM_WIDGET (im_widget_new ()); - else - im = IM_WIDGET (im_widget_new_with_first_message (message)); + /* Create the im object, first message must be created asynchronously */ + if (message) + imwidget = IM_WIDGET (im_widget_new ()); + else + imwidget = IM_WIDGET (im_widget_new_with_first_message (message)); - /* Keep a reference on this object in the call struct */ - tmp->_im_widget = im; - *call = tmp; + /* Keep a reference on this object in the call struct */ + // tmp->_im_widget = im; + // *call = tmp; - /* Update the widget with some useful call information: ie the call ID */ - im->call_id = tmp->_callID; + /* Update the widget with some useful call information: ie the call ID */ + imwidget->call_id = id; - /* Create the GtkInfoBar, used to display call information, and status of the IM widget */ - im_widget_infobar (im); + /* Create the GtkInfoBar, used to display call information, and status of the IM widget */ + im_widget_infobar (imwidget); - /* Add it to the main instant messaging window */ - im_window_add (im); + /* Add it to the main instant messaging window */ + im_window_add (GTK_WIDGET (imwidget)); - /* Update the first message to appears at widget creation*/ - im->first_message = g_strdup (message); + /* Update the first message to appears at widget creation*/ + if (message) + imwidget->first_message = g_strdup (message); - return FALSE; - } else { - DEBUG ("im widget exists for this call\n"); - im_window_show (); + if (from) + imwidget->first_message_from = g_strdup (from); - return TRUE; - } + *im = imwidget; + + return FALSE; + } else { + DEBUG ("im widget exists for this call\n"); + im_window_show (); + + return TRUE; } - return FALSE; + // } + + // return FALSE; } void @@ -371,13 +389,28 @@ im_widget_infobar (IMWidget *im) GtkWidget *infobar = im->info_bar; GtkWidget *content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar)); - /* Fetch call information */ + /* Fetch call/conference information */ callable_obj_t *im_widget_call = calllist_get (current_calls, im->call_id); + conference_obj_t *im_widget_conf = conferencelist_get (im->call_id); /* Create the label widgets with the call information saved in the IM Widget struct */ - gchar *msg1 = g_strdup_printf ("Calling %s %s", im_widget_call->_peer_number, im_widget_call->_peer_name); + gchar *msg1; + + if (im_widget_call) + msg1 = g_strdup_printf ("Calling %s %s", im_widget_call->_peer_number, im_widget_call->_peer_name); + else if (im_widget_conf) + msg1 = g_strdup_printf ("Conferencing"); // im_widget_conf->_confID); + else + msg1 = g_strdup (""); + GtkWidget *call_label = gtk_label_new (msg1); - im->info_state = call_state_image_widget (im_widget_call->_state); + + if (im_widget_call) + im->info_state = call_state_image_widget (im_widget_call->_state); + + if (im_widget_conf) + im->info_state = conf_state_image_widget (im_widget_conf->_state); + /* Add a nice icon from our own icon factory */ GtkWidget *logoUser = gtk_image_new_from_stock (GTK_STOCK_USER, GTK_ICON_SIZE_LARGE_TOOLBAR); @@ -404,10 +437,12 @@ call_state_image_widget (call_state_t state) switch (state) { case CALL_STATE_CURRENT: + case CALL_STATE_HOLD: + case CALL_STATE_RECORD: image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); break; default: - image = gtk_image_new_from_stock (GTK_STOCK_FAIL, GTK_ICON_SIZE_LARGE_TOOLBAR); + image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); break; } @@ -415,6 +450,27 @@ call_state_image_widget (call_state_t state) return image; } +GtkWidget* +conf_state_image_widget (conference_state_t state) +{ + + GtkWidget *image; + + switch (state) { + case CONFERENCE_STATE_ACTIVE_ATACHED: + case CONFERENCE_STATE_ACTIVE_DETACHED: + case CONFERENCE_STATE_RECORD: + case CONFERENCE_STATE_HOLD: + image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + default: + image = gtk_image_new_from_stock (GTK_STOCK_FAIL, GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + } + + return image; +} + void im_widget_update_state (IMWidget *im, gboolean active) { diff --git a/sflphone-client-gnome/src/widget/imwidget.h b/sflphone-client-gnome/src/widget/imwidget.h index f8816877e4c969f1030c37672cdcf80c9f2be8c3..ea0909f2135701b43e2951cad68821cd836ad722 100644 --- a/sflphone-client-gnome/src/widget/imwidget.h +++ b/sflphone-client-gnome/src/widget/imwidget.h @@ -34,6 +34,7 @@ #include <gtk/gtk.h> #include <callable_obj.h> #include <webkit/webkit.h> +#include <conference_obj.h> G_BEGIN_DECLS @@ -55,15 +56,18 @@ struct _IMWidget { GtkVBox parent_instance; /* Private */ + GtkWidget *tab; GtkWidget *textarea; GtkWidget *web_view; GtkWidget *info_bar; GtkWidget *info_state; gchar *call_id; - gchar *first_message; + gchar *first_message; // Message displayed at widget's creation time + gchar *first_message_from; // Sender of the first message (usefull in case of a conference) WebKitWebFrame *web_frame; // Our web frame JSGlobalContextRef js_context; // The frame's global JS context JSObjectRef js_global; // The frame's global context JS object + gboolean containText; }; struct _IMWidgetClass { @@ -75,9 +79,11 @@ struct _IMWidgetClass { @abstract Display the instant messaging interface for this call. If it has not been created yet, create it and attached it to the imWindow. @param A reference on the call attached to the current IM widget @param The first message to be displayed, webkit's frames are loaded asynchronously +@param The call id to be associated with the IMWidget +@param The first message sender, could be different of call id for conferences @return TRUE if window is already created, FALSE elsewhere */ -gboolean im_widget_display (callable_obj_t**, gchar*); +gboolean im_widget_display (IMWidget**, const gchar*, const gchar*, const gchar*); GType im_widget_get_type (void) G_GNUC_CONST; @@ -89,7 +95,7 @@ GtkWidget *im_widget_new (); /*! @function @abstract Create a new widget with first_message */ -GtkWidget *im_widget_new_with_first_message (gchar *message); +GtkWidget *im_widget_new_with_first_message (const gchar *message); /*! @function @@ -101,7 +107,7 @@ GtkWidget *im_widget_new_with_first_message (gchar *message); */ void im_widget_add_message (IMWidget *im, const gchar *from, const gchar *message, gint level); -void im_widget_send_message (callable_obj_t *call, const gchar *message); +void im_widget_send_message (gchar *id, const gchar *message); gchar* im_widget_add_message_time (); @@ -113,6 +119,8 @@ void im_widget_infobar (IMWidget *im); GtkWidget* call_state_image_widget (call_state_t state); +GtkWidget* conf_state_image_widget (conference_state_t state); + void im_widget_update_state (IMWidget *im, gboolean active); G_END_DECLS diff --git a/sflphone-common/configure.ac b/sflphone-common/configure.ac index 80790e831c78ce5cfa501b981af9816eed31f0f5..43799dccc018df0047bdbf7662f1fb9379db8948 100644 --- a/sflphone-common/configure.ac +++ b/sflphone-common/configure.ac @@ -52,7 +52,7 @@ AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([src/Makefile \ src/sip/Makefile \ - src/sip/im/Makefile \ + src/im/Makefile \ src/iax/Makefile \ src/audio/Makefile \ src/audio/audiortp/Makefile \ diff --git a/sflphone-common/src/Makefile.am b/sflphone-common/src/Makefile.am index fff35078332d02e3e7d3abe45747ec52f11dee66..c3b4b75d1f60103d26250161eec99f40d51f42a5 100644 --- a/sflphone-common/src/Makefile.am +++ b/sflphone-common/src/Makefile.am @@ -3,7 +3,7 @@ include ../globals.mak libexecdir=$(libdir)/sflphone libexec_PROGRAMS = sflphoned -SUBDIRS = dbus audio config plug-in hooks history sip iax +SUBDIRS = dbus audio config plug-in hooks history sip iax im # Add here the cpp files to be build with sflphone sflphoned_SOURCES = \ @@ -66,6 +66,7 @@ libsflphone_la_LIBADD = \ $(src)/libs/iax2/libiax2.la \ $(src)/libs/dbus-c++/src/libdbus-c++-1.la \ $(IAX_LIB) \ + ./im/libim.la \ ./sip/libsiplink.la \ ./audio/libaudio.la \ ./dbus/libdbus.la \ @@ -85,7 +86,8 @@ libsflphone_la_LDFLAGS = \ @SAMPLERATE_LIBS@ \ @libssl_LIBS@ \ @UUID_LIBS@ \ - @yaml_LIBS@ + @yaml_LIBS@ \ + @xml_LIBS@ libsflphone_la_CFLAGS = \ @CCGNU2_CFLAGS@ \ @@ -98,7 +100,8 @@ libsflphone_la_CFLAGS = \ @SAMPLERATE_CFLAGS@ \ @libssl_CFLAGS@ \ @UUID_CFLAGS@ \ - @yaml_CFLAGS@ + @yaml_CFLAGS@ \ + @xml_CFLAGS@ libsflphone_la_SOURCES = diff --git a/sflphone-common/src/account.h b/sflphone-common/src/account.h index fd75f67a67abf365aab9046bd59ab8ee44ae192c..793ddd5a806577bf616424db8dde8eb47fe869c6 100644 --- a/sflphone-common/src/account.h +++ b/sflphone-common/src/account.h @@ -163,8 +163,16 @@ class Account : public Serializable */ virtual ~Account(); + /** + * Method called by the configuration engine to serialize instance's information + * into configuration file. + */ virtual void serialize (Conf::YamlEmitter *emitter) = 0; + /** + * Method called by the configuration engine to restore instance internal state + * from configuration file. + */ virtual void unserialize (Conf::MappingNode *map) = 0; virtual void setAccountDetails (const std::map<std::string, std::string>& details) = 0; diff --git a/sflphone-common/src/audio/Makefile.am b/sflphone-common/src/audio/Makefile.am index 8f49c29cc547b096cd2a47d42a951fe4bbbc1c68..67542c2a6c469e6513d61c4d34187346bb7a668e 100644 --- a/sflphone-common/src/audio/Makefile.am +++ b/sflphone-common/src/audio/Makefile.am @@ -21,6 +21,7 @@ libaudio_la_SOURCES = \ delaydetection.cpp \ echocancel.cpp \ speexechocancel.cpp \ + noisesuppress.cpp \ audioprocessing.cpp \ dcblocker.cpp \ jitterbuf.cpp \ @@ -40,6 +41,7 @@ noinst_HEADERS = \ delaydetection.h \ echocancel.h \ speexechocancel.h \ + noisesuppress.h \ audioprocessing.h \ dcblocker.h \ jitterbuf.h \ diff --git a/sflphone-common/src/audio/alsa/alsalayer.cpp b/sflphone-common/src/audio/alsa/alsalayer.cpp index 895a072e7609a68d3007176795340073b69f18d9..83bec76ab13815ed782f5ff225cb920fe4f32745 100644 --- a/sflphone-common/src/audio/alsa/alsalayer.cpp +++ b/sflphone-common/src/audio/alsa/alsalayer.cpp @@ -62,8 +62,10 @@ AlsaLayer::AlsaLayer (ManagerImpl* manager) _audioPlugin = AudioLayer::_manager->audioPreference.getPlugin(); - AudioLayer::_echocancelstate = true; AudioLayer::_noisesuppressstate = true; + + // captureFile = new ofstream ("probeFile", ofstream::binary); + } // Destructor @@ -76,6 +78,9 @@ AlsaLayer::~AlsaLayer (void) delete _converter; _converter = NULL; } + + // captureFile->close(); + // delete captureFile; } bool @@ -95,6 +100,7 @@ AlsaLayer::closeLayer() throw; } + /* Then close the audio devices */ closeCaptureStream(); closePlaybackStream(); @@ -137,12 +143,6 @@ AlsaLayer::openDevice (int indexIn, int indexOut, int indexRing, int sampleRate, // use 1 sec buffer for resampling _converter = new SamplerateConverter (_audioSampleRate, 1000); - AudioLayer::_echoCancel = new EchoCancel(); - AudioLayer::_echoCanceller = new AudioProcessing (static_cast<Algorithm *> (_echoCancel)); - - AudioLayer::_echoCancel->setEchoCancelState (AudioLayer::_echocancelstate); - AudioLayer::_echoCancel->setNoiseSuppressState (AudioLayer::_noisesuppressstate); - AudioLayer::_dcblocker = new DcBlocker(); AudioLayer::_audiofilter = new AudioProcessing (static_cast<Algorithm *> (_dcblocker)); @@ -158,9 +158,6 @@ AlsaLayer::startStream (void) if (_audiofilter) _audiofilter->resetAlgorithm(); - if (_echoCanceller) - _echoCanceller->resetAlgorithm(); - if (is_playback_running() && is_capture_running()) return; @@ -255,22 +252,9 @@ bool AlsaLayer::isCaptureActive (void) return false; } - -void AlsaLayer::setEchoCancelState (bool state) -{ - // if a stream already running - if (AudioLayer::_echoCancel) - _echoCancel->setEchoCancelState (state); - - AudioLayer::_echocancelstate = state; -} - void AlsaLayer::setNoiseSuppressState (bool state) { // if a stream already opened - if (AudioLayer::_echoCancel) - _echoCancel->setNoiseSuppressState (state); - AudioLayer::_noisesuppressstate = state; } @@ -934,6 +918,7 @@ void AlsaLayer::audioCallback (void) // Urgent data (dtmf, incoming call signal) come first. toGet = (urgentAvailBytes < (int) (playbackAvailBytes)) ? urgentAvailBytes : playbackAvailBytes; out = (SFLDataFormat*) malloc (toGet); + memset (out, 0, toGet); if (out) { _urgentRingBuffer.Get (out, toGet, spkrVolume); @@ -953,6 +938,7 @@ void AlsaLayer::audioCallback (void) if (tone && (normalAvailBytes <= 0)) { out = (SFLDataFormat *) malloc (playbackAvailBytes); + memset (out, 0, playbackAvailBytes); if (out) { tone->getNext (out, playbackAvailSmpl, spkrVolume); @@ -965,6 +951,7 @@ void AlsaLayer::audioCallback (void) } else if (file_tone && !_RingtoneHandle && (normalAvailBytes <= 0)) { out = (SFLDataFormat *) malloc (playbackAvailBytes); + memset (out, 0, playbackAvailBytes); if (out) { file_tone->getNext (out, playbackAvailSmpl, spkrVolume); @@ -996,15 +983,12 @@ void AlsaLayer::audioCallback (void) toGet = (normalAvailBytes < (int) maxNbBytesToGet) ? normalAvailBytes : maxNbBytesToGet; out = (SFLDataFormat*) malloc (maxNbBytesToGet); + memset (out, 0, maxNbBytesToGet); if (normalAvailBytes) { getMainBuffer()->getData (out, toGet, spkrVolume); - // TODO: Audio processing should be performed inside mainbuffer - // to avoid such problem - AudioLayer::_echoCancel->setSamplingRate (_mainBufferSampleRate); - if (_mainBufferSampleRate && ( (int) _audioSampleRate != _mainBufferSampleRate)) { // Do sample rate conversion @@ -1012,6 +996,8 @@ void AlsaLayer::audioCallback (void) if (rsmpl_out) { rsmpl_out = (SFLDataFormat*) malloc (playbackAvailBytes); + memset (out, 0, playbackAvailBytes); + int nbSample = _converter->upsampleData ( (SFLDataFormat*) out, rsmpl_out, _mainBufferSampleRate, _audioSampleRate, nb_sample_down); write (rsmpl_out, nbSample*sizeof (SFLDataFormat), _PlaybackHandle); free (rsmpl_out); @@ -1025,9 +1011,6 @@ void AlsaLayer::audioCallback (void) } - // Copy far-end signal in echo canceller to adapt filter coefficient - // AudioLayer::_echoCanceller->putData (out, toGet); - } else { if (!tone && !file_tone) { @@ -1092,8 +1075,6 @@ void AlsaLayer::audioCallback (void) int toPut; SFLDataFormat* in = NULL; - SFLDataFormat echoCancelledMic[5000]; - memset (echoCancelledMic, 0, 5000); if (is_capture_running()) { @@ -1123,11 +1104,7 @@ void AlsaLayer::audioCallback (void) _audiofilter->processAudio (rsmpl_out, nbSample*sizeof (SFLDataFormat)); - // echo cancellation processing - // int sampleready = AudioLayer::_echoCanceller->processAudio (rsmpl_out, echoCancelledMic, nbSample*sizeof (SFLDataFormat)); - getMainBuffer()->putData (rsmpl_out, nbSample * sizeof (SFLDataFormat), 100); - // getMainBuffer()->putData (echoCancelledMic, sampleready*sizeof (SFLDataFormat), 100); free (rsmpl_out); rsmpl_out = 0; @@ -1139,8 +1116,7 @@ void AlsaLayer::audioCallback (void) if (filter_out) { _audiofilter->processAudio (in, filter_out, toPut); - // int sampleready = AudioLayer::_echoCanceller->processAudio (filter_out, echoCancelledMic, toPut); - // getMainBuffer()->putData (echoCancelledMic, sampleready*sizeof (SFLDataFormat), 100); + // captureFile->write ( (const char *) filter_out, toPut); getMainBuffer()->putData (filter_out, toPut, 100); free (filter_out); } diff --git a/sflphone-common/src/audio/alsa/alsalayer.h b/sflphone-common/src/audio/alsa/alsalayer.h index ca42673f39fbd198585b09b10fcf1bc7fd4819b0..7eebc11d5ad58121907eb137ec2215b4b2e8187b 100644 --- a/sflphone-common/src/audio/alsa/alsalayer.h +++ b/sflphone-common/src/audio/alsa/alsalayer.h @@ -37,6 +37,8 @@ #include "eventthread.h" #include <alsa/asoundlib.h> +// #include <fstream> + class RingBuffer; class ManagerImpl; @@ -157,20 +159,6 @@ class AlsaLayer : public AudioLayer bool isCaptureActive (void); - /** - * Get the echo canceller state - * @return true if echo cancel activated - */ - virtual bool getEchoCancelState (void) { - return AudioLayer::_echocancelstate; - } - - /** - * Set the echo canceller state - * @param state true if echocancel active, false elsewhere - */ - virtual void setEchoCancelState (bool state); - /** * Get the noise suppressor state * @return true if noise suppressor activated @@ -350,6 +338,8 @@ class AlsaLayer : public AudioLayer /** Sample rate converter object */ SamplerateConverter* _converter; + // ofstream *captureFile; + }; #endif // _ALSA_LAYER_H_ diff --git a/sflphone-common/src/audio/audiolayer.h b/sflphone-common/src/audio/audiolayer.h index 9859398d6728879af718bdb286c49ab2254bd3bf..62a4de683bad0f1d4dc2bbd5495f4592940bdd59 100644 --- a/sflphone-common/src/audio/audiolayer.h +++ b/sflphone-common/src/audio/audiolayer.h @@ -254,18 +254,6 @@ class AudioLayer return _recorder; } - /** - * Get the echo canceller state - * @return true if echo cancel activated - */ - virtual bool getEchoCancelState (void) = 0; - - /** - * Set the echo canceller state - * @param state true if echocancel active, false elsewhere - */ - virtual void setEchoCancelState (bool state) = 0; - /** * Get the noise suppressor state * @return true if noise suppressor activated @@ -367,14 +355,9 @@ class AudioLayer */ ost::Mutex _mutex; - EchoCancel *_echoCancel; - AudioProcessing *_echoCanceller; - DcBlocker *_dcblocker; AudioProcessing *_audiofilter; - bool _echocancelstate; - bool _noisesuppressstate; }; diff --git a/sflphone-common/src/audio/audiortp/AudioRtpSession.h b/sflphone-common/src/audio/audiortp/AudioRtpSession.h index 5db39baf38adda0e91c9dde019b97eb6f79f66fc..eb40be808df3ed35b5a97013c4da8a67290aa66b 100644 --- a/sflphone-common/src/audio/audiortp/AudioRtpSession.h +++ b/sflphone-common/src/audio/audiortp/AudioRtpSession.h @@ -54,9 +54,14 @@ #include "audio/jitterbuf.h" +#include <fstream> + // Frequency (in packet number) #define RTP_TIMESTAMP_RESET_FREQ 100 +// Factor use to increase volume in fade in +#define FADEIN_STEP_SIZE 4; + namespace sfl { @@ -79,6 +84,7 @@ typedef struct DtmfEvent { typedef list<DtmfEvent *> EventQueue; + template <typename D> class AudioRtpSession : public ost::Thread, public ost::TimerPort { @@ -121,18 +127,51 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort private: + /** + * Allocate memory for RTP buffers and fill them with zeros + */ void initBuffers (void); + /** + * Set RTP Sockets send/receive timeouts + */ void setSessionTimeouts (void); + + /** + * Set the audio codec for this RTP session + */ void setSessionMedia (AudioCodec*); + + /** + * Retreive destination address for this session. Stored in CALL + */ void setDestinationIpAddress (void); + /** + * Encode audio data from mainbuffer + */ int processDataEncode (void); + + /** + * Decode audio data received from peer + */ void processDataDecode (unsigned char * spkrData, unsigned int size); + /** + * Send encoded data to peer + */ void sendMicData(); + + /** + * Receive data from peer + */ void receiveSpeakerData (); + /** + * Ramp In audio data to avoid audio click from peer + */ + bool fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor); + ost::Time * _time; // This semaphore is not used @@ -235,11 +274,6 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ EventQueue _eventQueue; - /** - * Adaptive jitter buffer - */ - jitterbuf * _jbuffer; - /** * Packet size in ms */ @@ -252,8 +286,32 @@ class AudioRtpSession : public ost::Thread, public ost::TimerPort */ int _currentTime; + /** + * Preprocess internal data + */ SpeexPreprocessState *_noiseState; + /** + * State of mic fade in + */ + bool _micFadeInComplete; + + /** + * State of spkr fade in + */ + bool _spkrFadeInComplete; + + /** + * Ampliturde factor to fade in mic data + */ + SFLDataFormat _micAmplFactor; + + /** + * Amplitude factor to fade in spkr data + */ + SFLDataFormat _spkrAmplFactor; + + protected: SIPCall * _ca; @@ -282,8 +340,11 @@ AudioRtpSession<D>::AudioRtpSession (ManagerImpl * manager, SIPCall * sipcall) : _timestampIncrement (0), _timestampCount (0), _countNotificationTime (0), - _jbuffer (NULL), _noiseState (NULL), + _micFadeInComplete (false), + _spkrFadeInComplete (false), + _micAmplFactor (32000), + _spkrAmplFactor (32000), _ca (sipcall) { setCancel (cancelDefault); @@ -302,15 +363,10 @@ AudioRtpSession<D>::AudioRtpSession (ManagerImpl * manager, SIPCall * sipcall) : _layerFrameSize = _audiolayer->getFrameSize(); // in ms _layerSampleRate = _audiolayer->getSampleRate(); - _jbuffer = jb_new(); - - _jbuffer->info.conf.max_jitterbuf = 1000; - _jbuffer->info.conf.target_extra = 100; - _jbuffer->info.silence_begin_ts = 0; - _ts= 0; _packetLength = 20; _currentTime = 0; + } template <typename D> @@ -360,10 +416,6 @@ AudioRtpSession<D>::~AudioRtpSession() _audiocodec = NULL; } - if (_jbuffer) { - jb_destroy (_jbuffer); - } - if (_noiseState) { speex_preprocess_state_destroy (_noiseState); } @@ -386,16 +438,16 @@ void AudioRtpSession<D>::initBuffers() int nbSamplesMax = (int) (_codecSampleRate * _layerFrameSize /1000) *2; _micData = new SFLDataFormat[nbSamplesMax]; _micDataConverted = new SFLDataFormat[nbSamplesMax]; - _micDataEncoded = new unsigned char[nbSamplesMax]; + _micDataEncoded = new unsigned char[nbSamplesMax*2]; _spkrDataConverted = new SFLDataFormat[nbSamplesMax]; _spkrDataDecoded = new SFLDataFormat[nbSamplesMax]; - memset (_micData, 0, nbSamplesMax); - memset (_micDataConverted, 0, nbSamplesMax); - memset (_micDataEncoded, 0, nbSamplesMax); - memset (_spkrDataConverted, 0, nbSamplesMax); - memset (_spkrDataDecoded, 0, nbSamplesMax); + memset (_micData, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_micDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_micDataEncoded, 0, nbSamplesMax*2); + memset (_spkrDataConverted, 0, nbSamplesMax*sizeof (SFLDataFormat)); + memset (_spkrDataDecoded, 0, nbSamplesMax*sizeof (SFLDataFormat)); _manager->addStream (_ca->getCallId()); } @@ -613,6 +665,12 @@ int AudioRtpSession<D>::processDataEncode (void) // Get bytes from micRingBuffer to data_from_mic int nbSample = _manager->getAudioDriver()->getMainBuffer()->getData (_micData , bytesAvail, 100, _ca->getCallId()) / sizeof (SFLDataFormat); + if (!_micFadeInComplete) + _micFadeInComplete = fadeIn (_micData, nbSample, &_micAmplFactor); + + if (nbSample == 0) + return nbSample; + // nb bytes to be sent over RTP int compSize = 0; @@ -624,14 +682,15 @@ int AudioRtpSession<D>::processDataEncode (void) nbSample = _converter->downsampleData (_micData , _micDataConverted , _audiocodec->getClockRate(), _mainBufferSampleRate, nb_sample_up); - compSize = _audiocodec->codecEncode (_micDataEncoded, _micDataConverted, nbSample*sizeof (int16)); + compSize = _audiocodec->codecEncode (_micDataEncoded, _micDataConverted, nbSample*sizeof (SFLDataFormat)); } else { _nSamplesMic = nbSample; // no resampling required - compSize = _audiocodec->codecEncode (_micDataEncoded, _micData, nbSample*sizeof (int16)); + compSize = _audiocodec->codecEncode (_micDataEncoded, _micData, nbSample*sizeof (SFLDataFormat)); + } return compSize; @@ -640,7 +699,6 @@ int AudioRtpSession<D>::processDataEncode (void) template <typename D> void AudioRtpSession<D>::processDataDecode (unsigned char * spkrData, unsigned int size) { - if (_audiocodec != NULL) { @@ -652,6 +710,9 @@ void AudioRtpSession<D>::processDataDecode (unsigned char * spkrData, unsigned i // buffer _receiveDataDecoded ----> short int or int16, coded on 2 bytes int nbSample = expandedSize / sizeof (SFLDataFormat); + if (!_spkrFadeInComplete) + _spkrFadeInComplete = fadeIn (_spkrDataDecoded, nbSample, &_spkrAmplFactor); + // test if resampling is required if (_audiocodec->getClockRate() != _mainBufferSampleRate) { @@ -751,36 +812,44 @@ void AudioRtpSession<D>::receiveSpeakerData () unsigned char* spkrDataIn = NULL; unsigned int size = 0; - int result; - - jb_frame frame; - - _jbuffer->info.conf.resync_threshold = 0; if (adu) { spkrDataIn = (unsigned char*) adu->getData(); // data in char size = adu->getSize(); // size in char - result = jb_put (_jbuffer, spkrDataIn, JB_TYPE_VOICE, _packetLength, _ts+=20, _currentTime); - } else { _debug ("No RTP packet available !!!!!!!!!!!!!!!!!!!!!!!\n"); } - result = jb_get (_jbuffer, &frame, _currentTime+=20, _packetLength); - // DTMF over RTP, size must be over 4 in order to process it as voice data if (size > 4) { processDataDecode (spkrDataIn, size); - //if(result == JB_OK) { - // processDataDecode((unsigned char *)(frame.data), 160); - //} } delete adu; } +template <typename D> +bool AudioRtpSession<D>::fadeIn (SFLDataFormat *audio, int size, SFLDataFormat *factor) +{ + + // apply amplitude factor; + while (size) { + size--; + audio[size] /= *factor; + } + + // decrease factor + *factor /= FADEIN_STEP_SIZE; + + // if factor reach 0, thsi function should no be called anymore + if (*factor == 0) + return true; + + return false; +} + template <typename D> int AudioRtpSession<D>::startRtpThread (AudioCodec* audiocodec) { @@ -822,6 +891,8 @@ void AudioRtpSession<D>::run () _debug ("RTP: Entering mainloop for call %s",_ca->getCallId().c_str()); + _manager->getAudioDriver()->getMainBuffer()->getInternalSamplingRate(); + while (!testCancel()) { // Reset timestamp to make sure the timing information are up to date diff --git a/sflphone-common/src/audio/noisesuppress.cpp b/sflphone-common/src/audio/noisesuppress.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf27eb352241f423f4889a30176d5fd10211cbac --- /dev/null +++ b/sflphone-common/src/audio/noisesuppress.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. + * Author: Alexandre Savard <alexandre.savard@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. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "noisesuppress.h" + +NoiseSuppress::NoiseSuppress (int smplPerFrame, int samplingRate) : _noiseState (NULL) + , _smplPerFrame (smplPerFrame) + , _samplingRate (samplingRate) +{ + initNewNoiseSuppressor (_smplPerFrame, _samplingRate); +} + + +NoiseSuppress::~NoiseSuppress() +{ + speex_preprocess_state_destroy (_noiseState); +} + +void NoiseSuppress::reset (void) +{ + + speex_preprocess_state_destroy (_noiseState); + + initNewNoiseSuppressor (_smplPerFrame, _samplingRate); +} + +void NoiseSuppress::putData (SFLDataFormat *inputData, int nbBytes) {} + +int NoiseSuppress::getData (SFLDataFormat *outputData) {} + +void NoiseSuppress::process (SFLDataFormat *data, int nbBytes) +{ + if (_noiseState) + speex_preprocess_run (_noiseState, data); +} + +int NoiseSuppress::process (SFLDataFormat *inputData, SFLDataFormat *outputData, int nbBytes) {} + +void NoiseSuppress::process (SFLDataFormat *micData, SFLDataFormat *spkrData, SFLDataFormat *outputData, int nbBytes) {} + +void NoiseSuppress::initNewNoiseSuppressor (int smplPerFrame, int samplingRate) +{ + _noiseState = speex_preprocess_state_init (smplPerFrame, samplingRate); + int i=1; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_DENOISE, &i); + i=-20; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &i); + i=0; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_AGC, &i); + i=8000; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_AGC_TARGET, &i); + i=16000; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i); + i=0; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_DEREVERB, &i); + float f=0.0; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f); + f=0.0; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f); + i = 0; + speex_preprocess_ctl (_noiseState, SPEEX_PREPROCESS_SET_VAD, &i); +} diff --git a/sflphone-common/src/audio/noisesuppress.h b/sflphone-common/src/audio/noisesuppress.h new file mode 100644 index 0000000000000000000000000000000000000000..fe9494013073d7a75c5e89618e7526ac2e03da07 --- /dev/null +++ b/sflphone-common/src/audio/noisesuppress.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. + * Author: Alexandre Savard <alexandre.savard@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. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef NOISESUPPRESS_H +#define NOISESUPPRESS_H + +#include <speex/speex_preprocess.h> +#include "algorithm.h" +#include "audioprocessing.h" + + +class NoiseSuppress : public Algorithm +{ + + public: + + NoiseSuppress (int smplPerFrame, int samplingRate); + + ~NoiseSuppress (void); + + /** + * Reset noise suppressor internal state at runtime. Usefull when making a new call + */ + virtual void reset (void); + + /** + * Unused + */ + virtual void putData (SFLDataFormat *inputData, int nbBytes); + + /** + * Unused + */ + virtual int getData (SFLDataFormat *outputData); + + /** + * Unused + */ + virtual void process (SFLDataFormat *data, int nbBytes); + + /** + * Unused + */ + virtual int process (SFLDataFormat *inputData, SFLDataFormat *outputData, int nbBytes); + + /** + * Unused + */ + virtual void process (SFLDataFormat *micData, SFLDataFormat *spkrData, SFLDataFormat *outputData, int nbBytes); + + private: + + void initNewNoiseSuppressor (int _smplPerFrame, int samplingRate); + + /** + * Noise reduction processing state + */ + SpeexPreprocessState *_noiseState; + + int _smplPerFrame; + + int _samplingRate; + +}; + +#endif diff --git a/sflphone-common/src/audio/pulseaudio/pulselayer.cpp b/sflphone-common/src/audio/pulseaudio/pulselayer.cpp index 84044628336dc56c68cddef637126790f6dd786a..e90d0698f1a5d988ff171bba01697bbb4d3bf669 100644 --- a/sflphone-common/src/audio/pulseaudio/pulselayer.cpp +++ b/sflphone-common/src/audio/pulseaudio/pulselayer.cpp @@ -33,9 +33,13 @@ #include "managerimpl.h" +// #include <fstream> + int framesPerBuffer = 2048; + + static void playback_callback (pa_stream* s, size_t bytes, void* userdata) { @@ -161,7 +165,7 @@ static void source_input_info_callback (pa_context *c UNUSED, const pa_source_in } -static void context_changed_callback (pa_context* c, pa_subscription_event_type_t t, uint32_t idx, void* userdata) +static void context_changed_callback (pa_context* c, pa_subscription_event_type_t t, uint32_t idx UNUSED, void* userdata) { switch (t) { @@ -241,16 +245,14 @@ PulseLayer::PulseLayer (ManagerImpl* manager) is_started = false; - AudioLayer::_echocancelstate = true; AudioLayer::_noisesuppressstate = true; byteCounter = 0; - /* - captureFile = new ofstream("captureFile", ofstream::binary); - captureRsmplFile = new ofstream("captureRsmplFile", ofstream::binary); - captureFilterFile = new ofstream("captureFilterFile", ofstream::binary); - */ + + // captureFile = new ofstream ("probeCaptureFile", ofstream::binary); + // spkrFile = new ofstream ("probeSpkrFile", ofstream::binary); + openLayer(); } @@ -264,27 +266,18 @@ PulseLayer::~PulseLayer (void) _converter = NULL; } - delete AudioLayer::_echoCancel; - AudioLayer::_echoCancel = NULL; - - delete AudioLayer::_echoCanceller; - AudioLayer::_echoCanceller = NULL; - delete AudioLayer::_dcblocker; AudioLayer::_dcblocker = NULL; delete AudioLayer::_audiofilter; AudioLayer::_audiofilter = NULL; - /* - captureFile->close(); - captureRsmplFile->close(); - captureFilterFile->close(); - delete captureFile; - delete captureRsmplFile; - delete captureFilterFile; - */ + // captureFile->close(); + // spkrFile->close(); + + // delete captureFile; + // delete spkrFile; } void @@ -436,9 +429,6 @@ bool PulseLayer::openDevice (int indexIn UNUSED, int indexOut UNUSED, int indexR _converter = new SamplerateConverter (_audioSampleRate, 1000); // Instantiate the algorithm - AudioLayer::_echoCancel = new EchoCancel (_audioSampleRate, _frameSize); - AudioLayer::_echoCanceller = new AudioProcessing (static_cast<Algorithm *> (_echoCancel)); - AudioLayer::_dcblocker = new DcBlocker(); AudioLayer::_audiofilter = new AudioProcessing (static_cast<Algorithm *> (_dcblocker)); @@ -649,11 +639,6 @@ int PulseLayer::canGetMic() void PulseLayer::startStream (void) { - if (_audiofilter) - _audiofilter->resetAlgorithm(); - - if (_echoCanceller) - _echoCanceller->resetAlgorithm(); // Create Streams if (!playback || !record) @@ -749,21 +734,9 @@ void PulseLayer::processData (void) } -void PulseLayer::setEchoCancelState (bool state) -{ - // if a stream already running - if (AudioLayer::_echoCancel) - _echoCancel->setEchoCancelState (state); - - AudioLayer::_echocancelstate = state; -} - void PulseLayer::setNoiseSuppressState (bool state) { // if a stream already opened - if (AudioLayer::_echoCancel) - _echoCancel->setNoiseSuppressState (state); - AudioLayer::_noisesuppressstate = state; } @@ -792,8 +765,11 @@ void PulseLayer::writeToSpeaker (void) if (urgentAvailBytes > writeableSize) { out = (SFLDataFormat*) pa_xmalloc (writeableSize); + memset (out, 0, writeableSize); + _urgentRingBuffer.Get (out, writeableSize, 100); + // spkrFile->write ( (const char *) out, writeableSize); pa_stream_write (playback->pulseStream(), out, writeableSize, NULL, 0, PA_SEEK_RELATIVE); pa_xfree (out); @@ -818,8 +794,11 @@ void PulseLayer::writeToSpeaker (void) if (playback->getStreamState() == PA_STREAM_READY) { out = (SFLDataFormat*) pa_xmalloc (writeableSize); + memset (out, 0, writeableSize); + int copied = tone->getNext (out, writeableSize / sizeof (SFLDataFormat), 100); + //spkrFile->write ( (const char *) out, copied*sizeof (SFLDataFormat)); pa_stream_write (playback->pulseStream(), out, copied * sizeof (SFLDataFormat), NULL, 0, PA_SEEK_RELATIVE); pa_xfree (out); @@ -857,17 +836,15 @@ void PulseLayer::writeToSpeaker (void) byteToGet = byteToGet-1; out = (SFLDataFormat*) pa_xmalloc (maxNbBytesToGet); + memset (out, 0, maxNbBytesToGet); getMainBuffer()->getData (out, byteToGet, 100); - // TODO: Audio processing should be performed inside mainbuffer - // to avoid such problem - AudioLayer::_echoCancel->setSamplingRate (_mainBufferSampleRate); - // test if resampling is required if (_mainBufferSampleRate && ( (int) _audioSampleRate != _mainBufferSampleRate)) { SFLDataFormat* rsmpl_out = (SFLDataFormat*) pa_xmalloc (writeableSize); + memset (out, 0, writeableSize); // Do sample rate conversion int nb_sample_down = byteToGet / sizeof (SFLDataFormat); @@ -877,19 +854,17 @@ void PulseLayer::writeToSpeaker (void) if ( (nbSample*sizeof (SFLDataFormat)) > (unsigned int) writeableSize) _warn ("Audio: Error: nbsbyte exceed buffer length"); + // spkrFile->write ( (const char *) out, nbSample*sizeof (SFLDataFormat)); pa_stream_write (playback->pulseStream(), rsmpl_out, nbSample*sizeof (SFLDataFormat), NULL, 0, PA_SEEK_RELATIVE); pa_xfree (rsmpl_out); } else { - + // spkrFile->write ( (const char *) out, byteToGet); pa_stream_write (playback->pulseStream(), out, byteToGet, NULL, 0, PA_SEEK_RELATIVE); - } - - // Copy far-end signal in echo canceller to adapt filter coefficient - // AudioLayer::_echoCanceller->putData(out, byteToGet); + } pa_xfree (out); @@ -898,9 +873,9 @@ void PulseLayer::writeToSpeaker (void) if (tone == 0) { SFLDataFormat* zeros = (SFLDataFormat*) pa_xmalloc (writeableSize); - bzero (zeros, writeableSize); + // spkrFile->write ( (const char *) zeros, writeableSize); pa_stream_write (playback->pulseStream(), zeros, writeableSize, NULL, 0, PA_SEEK_RELATIVE); pa_xfree (zeros); @@ -922,9 +897,6 @@ void PulseLayer::readFromMic (void) const char* data = NULL; size_t r; - SFLDataFormat echoCancelledMic[10000]; - memset (echoCancelledMic, 0, 10000*sizeof (SFLDataFormat)); - int readableSize = pa_stream_readable_size (record->pulseStream()); @@ -940,13 +912,12 @@ void PulseLayer::readFromMic (void) if (_mainBufferSampleRate && ( (int) _audioSampleRate != _mainBufferSampleRate)) { SFLDataFormat* rsmpl_out = (SFLDataFormat*) pa_xmalloc (readableSize); + memset (rsmpl_out, 0, readableSize); int nbSample = r / sizeof (SFLDataFormat); int nb_sample_up = nbSample; - // captureFile->write ((const char *)data, nbSample*sizeof(SFLDataFormat)); - nbSample = _converter->downsampleData ( (SFLDataFormat *) data, rsmpl_out, _mainBufferSampleRate, _audioSampleRate, nb_sample_up); // captureRsmplFile->write ((const char *)rsmpl_out, nbSample*sizeof(SFLDataFormat)); @@ -956,28 +927,21 @@ void PulseLayer::readFromMic (void) // captureFilterFile->write ((const char *)rsmpl_out, nbSample*sizeof(SFLDataFormat)); - // echo cancellation processing - // int sampleready = _echoCanceller->processAudio(rsmpl_out, echoCancelledMic, nbSample*sizeof(SFLDataFormat)); - - // getMainBuffer()->putData ( (void*) rsmpl_out, nbSample*sizeof (SFLDataFormat), 100); - // if(sampleready) - // getMainBuffer()->putData ( echoCancelledMic, sampleready*sizeof (SFLDataFormat), 100); getMainBuffer()->putData (rsmpl_out, nbSample*sizeof (SFLDataFormat), 100); pa_xfree (rsmpl_out); } else { + SFLDataFormat* filter_out = (SFLDataFormat*) pa_xmalloc (r); + memset (filter_out, 0, r); // remove dc offset _audiofilter->processAudio ( (SFLDataFormat *) data, filter_out, r); - // echo cancellation processing - // int sampleready = _echoCanceller->processAudio((SFLDataFormat *)filter_out, echoCancelledMic, r); + // captureFile->write ( (const char *) filter_out, r); - // no resampling required - // getMainBuffer()->putData (echoCancelledMic, sampleready*sizeof (SFLDataFormat), 100); getMainBuffer()->putData (filter_out, r, 100); pa_xfree (filter_out); @@ -1007,6 +971,8 @@ void PulseLayer::ringtoneToSpeaker (void) if (ringtone->getStreamState() == PA_STREAM_READY) { out = (SFLDataFormat *) pa_xmalloc (writableSize); + memset (out, 0, writableSize); + int copied = file_tone->getNext (out, writableSize/sizeof (SFLDataFormat), 100); pa_stream_write (ringtone->pulseStream(), out, copied*sizeof (SFLDataFormat), NULL, 0, PA_SEEK_RELATIVE); @@ -1018,6 +984,7 @@ void PulseLayer::ringtoneToSpeaker (void) out = (SFLDataFormat*) pa_xmalloc (writableSize); memset (out, 0, writableSize); + pa_stream_write (ringtone->pulseStream(), out, writableSize, NULL, 0, PA_SEEK_RELATIVE); pa_xfree (out); diff --git a/sflphone-common/src/audio/pulseaudio/pulselayer.h b/sflphone-common/src/audio/pulseaudio/pulselayer.h index 2cfb2595d17e473c2b4fa9b8e9aafd166c94e061..d06bec75b07fb5855692b9f3735ca92f50caf450 100644 --- a/sflphone-common/src/audio/pulseaudio/pulselayer.h +++ b/sflphone-common/src/audio/pulseaudio/pulselayer.h @@ -191,20 +191,6 @@ class PulseLayer : public AudioLayer void processData (void); - /** - * Get the echo canceller state - * @return true if echo cancel activated - */ - bool getEchoCancelState (void) { - return AudioLayer::_echocancelstate; - } - - /** - * Set the echo canceller state - * @param state true if echocancel active, false elsewhere - */ - void setEchoCancelState (bool state); - /** * Get the noise suppressor state * @return true if noise suppressor activated @@ -292,11 +278,9 @@ class PulseLayer : public AudioLayer int spkrVolume; int micVolume; - /* - ofstream *captureFile; - ofstream *captureRsmplFile; - ofstream *captureFilterFile; - */ + + // ofstream *captureFile; + // ofstream *spkrFile; DeviceList _sinkList; diff --git a/sflphone-common/src/audio/recordable.cpp b/sflphone-common/src/audio/recordable.cpp index 87c3f2b302aac7020335f3d25d5cbfdfa41f2c12..d0ed8bf1316fb16bda5a27daea514328ec18aa11 100644 --- a/sflphone-common/src/audio/recordable.cpp +++ b/sflphone-common/src/audio/recordable.cpp @@ -48,10 +48,9 @@ Recordable::~Recordable() } -void Recordable::initRecFileName() +void Recordable::initRecFileName (std::string filename) { - - recAudio.initFileName (getRecFileId()); + recAudio.initFileName (filename); } diff --git a/sflphone-common/src/audio/recordable.h b/sflphone-common/src/audio/recordable.h index 4ec7362afe78e39d127700d6632b423e43b9da03..9a99724217bff8900de6e2a9a76c535a58afb2d4 100644 --- a/sflphone-common/src/audio/recordable.h +++ b/sflphone-common/src/audio/recordable.h @@ -65,7 +65,7 @@ class Recordable /** * Init the recording file name according to path specified in configuration */ - void initRecFileName(); + void initRecFileName (std::string filename); /** * Set recording sampling rate. diff --git a/sflphone-common/src/audio/samplerateconverter.cpp b/sflphone-common/src/audio/samplerateconverter.cpp index 320d70b5cd1396dc331267993d66230de828e45f..c491c865a12a051c113f56dcc7ac86b7af70fe4a 100644 --- a/sflphone-common/src/audio/samplerateconverter.cpp +++ b/sflphone-common/src/audio/samplerateconverter.cpp @@ -30,20 +30,6 @@ #include "samplerateconverter.h" #include "manager.h" -SamplerateConverter::SamplerateConverter (void) - : _frequence (Manager::instance().getConfigInt (AUDIO , AUDIO_SAMPLE_RATE)) //44100 - , _framesize (Manager::instance().getConfigInt (AUDIO , ALSA_FRAME_SIZE)) - , _floatBufferDownMic (NULL) - , _floatBufferUpMic (NULL) - , _src_state_mic (NULL) - , _floatBufferDownSpkr (NULL) - , _floatBufferUpSpkr (NULL) - , _src_state_spkr (NULL) - , _src_err (0) -{ - init(); -} - SamplerateConverter::SamplerateConverter (int freq , int fs) : _frequence (freq) , _framesize (fs) @@ -61,19 +47,36 @@ SamplerateConverter::SamplerateConverter (int freq , int fs) SamplerateConverter::~SamplerateConverter (void) { - delete [] _floatBufferUpMic; - _floatBufferUpMic = NULL; - delete [] _floatBufferDownMic; - _floatBufferDownMic = NULL; + if (_floatBufferUpMic) { + delete [] _floatBufferUpMic; + _floatBufferUpMic = NULL; + } + + if (_floatBufferDownMic) { + delete [] _floatBufferDownMic; + _floatBufferDownMic = NULL; + } + + if (_floatBufferUpSpkr) { + delete [] _floatBufferUpSpkr; + _floatBufferUpSpkr = NULL; + } - delete [] _floatBufferUpSpkr; - _floatBufferUpSpkr = NULL; - delete [] _floatBufferDownSpkr; - _floatBufferDownSpkr = NULL; + if (_floatBufferDownSpkr) { + delete [] _floatBufferDownSpkr; + _floatBufferDownSpkr = NULL; + } // libSamplerateConverter-related - _src_state_mic = src_delete (_src_state_mic); - _src_state_spkr = src_delete (_src_state_spkr); + if (_src_state_mic) { + _src_state_mic = src_delete (_src_state_mic); + _src_state_mic = NULL; + } + + if (_src_state_spkr) { + _src_state_spkr = src_delete (_src_state_spkr); + _src_state_spkr = NULL; + } } void SamplerateConverter::init (void) diff --git a/sflphone-common/src/audio/samplerateconverter.h b/sflphone-common/src/audio/samplerateconverter.h index 5645a64a4bc8d9a93a7123aa37c4764113e43515..f3616b65f12568a945bd6b2951cbca234dc2b747 100644 --- a/sflphone-common/src/audio/samplerateconverter.h +++ b/sflphone-common/src/audio/samplerateconverter.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc. * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@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 @@ -39,9 +40,15 @@ class SamplerateConverter { public: - /** Constructor */ - SamplerateConverter (void); - SamplerateConverter (int freq , int fs); + /** + * Samplerate converter is used for several situation: + * streaming conversion (RTP, IAX), audiolayer conversion, + * audio files conversion. Parameters are used to compute + * internal buffer size. Converter must be reinitialized + * every time these parameters change + */ + SamplerateConverter (int freq=44100, int frameSize=20); + /** Destructor */ ~SamplerateConverter (void); diff --git a/sflphone-common/src/call.h b/sflphone-common/src/call.h index f7fb935f167d4320fdc703c729bd3fcb67bc3a52..712e60d58892b75ed745ff4334123df56de1d10e 100644 --- a/sflphone-common/src/call.h +++ b/sflphone-common/src/call.h @@ -271,7 +271,7 @@ class Call: public Recordable } std::string getFileName (void) { - return _filename; + return _peerNumber; } virtual bool setRecording (void); @@ -323,7 +323,7 @@ class Call: public Recordable /** Display Name */ std::string _displayName; - /** File name for his call : time YY-MM-DD */ + /** File name for his call, should be peer number */ std::string _filename; diff --git a/sflphone-common/src/conference.cpp b/sflphone-common/src/conference.cpp index 3e645628ddc70a52c88fac9f0337a1380e66f7b7..044b889f845afc9effc7f8ff14d87c4ba66acb88 100644 --- a/sflphone-common/src/conference.cpp +++ b/sflphone-common/src/conference.cpp @@ -55,7 +55,7 @@ Conference::Conference() _nbParticipant = 0; _id = conf.append (s); - Recordable::initRecFileName(); + Recordable::initRecFileName (_id); } diff --git a/sflphone-common/src/config/yamlemitter.cpp b/sflphone-common/src/config/yamlemitter.cpp index 3b6ba5c0c35d50e2e6d6b8a0071239f9d380a424..0f07f777538417112d7c1f6723238ef5f559e3cd 100644 --- a/sflphone-common/src/config/yamlemitter.cpp +++ b/sflphone-common/src/config/yamlemitter.cpp @@ -47,7 +47,8 @@ YamlEmitter::~YamlEmitter() void YamlEmitter::open() { - fd = fopen (filename.c_str(), "wb"); + + fd = fopen (filename.c_str(), "w"); if (!fd) throw YamlEmitterException ("Could not open file descriptor"); @@ -55,12 +56,13 @@ void YamlEmitter::open() if (!yaml_emitter_initialize (&emitter)) throw YamlEmitterException ("Could not initialize emitter"); - // Use unicode format + // Allows unescaped unicode characters yaml_emitter_set_unicode (&emitter, 1); yaml_emitter_set_output_file (&emitter, fd); - yaml_document_initialize (&document, NULL, NULL, NULL, 0, 0); + if (!yaml_document_initialize (&document, NULL, NULL, NULL, 0, 0)) + throw YamlEmitterException ("Could not initialize yaml document while saving configuration"); // Init the main configuration mapping if ( (topLevelMapping = yaml_document_add_mapping (&document, NULL, YAML_BLOCK_MAPPING_STYLE)) == 0) @@ -74,13 +76,13 @@ void YamlEmitter::close() if (!fd) throw YamlEmitterException ("File descriptor not valid"); - fclose (fd); - /* - if(!fclose(fd)) - throw YamlEmitterException("Error closing file descriptor"); - */ - yaml_document_delete (&document); + if (fclose (fd)) + throw YamlEmitterException ("Error closing file descriptor"); + + + _debug ("Config: Configuration file closed successfully"); + } void YamlEmitter::read() {} @@ -92,7 +94,9 @@ void YamlEmitter::write() void YamlEmitter::serializeData() { - yaml_emitter_dump (&emitter, &document); + // Document object is destroyed once its content is emitted + if (!yaml_emitter_dump (&emitter, &document)) + throw YamlEmitterException ("Error while emitting configuration yaml document"); } diff --git a/sflphone-common/src/config/yamlparser.cpp b/sflphone-common/src/config/yamlparser.cpp index b4d8c5de47ac3afd76f36f778bbffa970b7ff614..90b46c8470c39bdab5774ddd84578b4b647dc5aa 100644 --- a/sflphone-common/src/config/yamlparser.cpp +++ b/sflphone-common/src/config/yamlparser.cpp @@ -79,9 +79,9 @@ void YamlParser::close() if (!fd) throw YamlParserException ("File descriptor not valid"); - fclose (fd); - // if(!fclose(fd)) - // throw YamlParserException("Error closing file descriptor"); + // fclose (fd); + if (fclose (fd)) + throw YamlParserException ("Error closing file descriptor"); } diff --git a/sflphone-common/src/dbus/callmanager.cpp b/sflphone-common/src/dbus/callmanager.cpp index a5498d0e6488c756ceecf97a173fa93aaf7bda8b..26eaa53fda60c725c18891240c8dfa32d03da8f5 100644 --- a/sflphone-common/src/dbus/callmanager.cpp +++ b/sflphone-common/src/dbus/callmanager.cpp @@ -464,6 +464,6 @@ CallManager::setPBXEnrollment (const std::string& callID, const bool& yesNo) void CallManager::sendTextMessage (const std::string& callID, const std::string& message) { - if (!Manager::instance().sendTextMessage (callID, message)) + if (!Manager::instance().sendTextMessage (callID, message, "Me")) throw CallManagerException(); } diff --git a/sflphone-common/src/dbus/configurationmanager-introspec.xml b/sflphone-common/src/dbus/configurationmanager-introspec.xml index c2818831f9e18520fe3bbfbe9c0982bd6a2133a2..f051f3b44bfb685a4408e18bf578024f49831f75 100755 --- a/sflphone-common/src/dbus/configurationmanager-introspec.xml +++ b/sflphone-common/src/dbus/configurationmanager-introspec.xml @@ -545,22 +545,6 @@ </arg> </method> - <method name="getEchoCancelState" tp:name-for-bindings="getEchoCancelState"> - <tp:docstring> - </tp:docstring> - <arg type="s" name="state" direction="out"> - <tp:docstring> - </tp:docstring> - </arg> - </method> - - <method name="setEchoCancelState" tp:name-for-bindings="setEchoCancelState"> - <tp:docstring> - </tp:docstring> - <arg type="s" name="state" direction="in"> - </arg> - </method> - <method name="getNoiseSuppressState" tp:name-for-bindings="getNoiseSuppressState"> <tp:docstring> </tp:docstring> diff --git a/sflphone-common/src/dbus/configurationmanager.cpp b/sflphone-common/src/dbus/configurationmanager.cpp index c14c491bb55e2b18b4777da63bcd2944244be96c..2273ce8381b24e44df1c198007ab245d28d43d98 100644 --- a/sflphone-common/src/dbus/configurationmanager.cpp +++ b/sflphone-common/src/dbus/configurationmanager.cpp @@ -332,8 +332,16 @@ int32_t ConfigurationManager::getNumberOfCredential ( const std::string& accountID) { - SIPAccount *sipaccount = (SIPAccount *) Manager::instance().getAccount (accountID); - return sipaccount->getCredentialCount(); + Account *account = Manager::instance().getAccount (accountID); + + if (!account) + return 0; + + if (account->getType() == "SIP") { + SIPAccount *sipaccount = static_cast<SIPAccount *> (account); + return sipaccount->getCredentialCount(); + } else + return 0; } void ConfigurationManager::setCredential (const std::string& accountID, @@ -550,16 +558,6 @@ std::string ConfigurationManager::getCurrentAudioOutputPlugin (void) return Manager::instance().getCurrentAudioOutputPlugin(); } -std::string ConfigurationManager::getEchoCancelState (void) -{ - return Manager::instance().getEchoCancelState(); -} - -void ConfigurationManager::setEchoCancelState (const std::string& state) -{ - Manager::instance().setEchoCancelState (state); -} - std::string ConfigurationManager::getNoiseSuppressState (void) { return Manager::instance().getNoiseSuppressState(); diff --git a/sflphone-common/src/dbus/configurationmanager.h b/sflphone-common/src/dbus/configurationmanager.h index 469114922870232c8fceb5267f40dec3b71bd5cd..df510731c0ee45c3b220e6f3e77a132739fcdd6f 100644 --- a/sflphone-common/src/dbus/configurationmanager.h +++ b/sflphone-common/src/dbus/configurationmanager.h @@ -89,8 +89,6 @@ class ConfigurationManager std::vector< std::string > getCurrentAudioDevicesIndex(); int32_t getAudioDeviceIndex (const std::string& name); std::string getCurrentAudioOutputPlugin (void); - std::string getEchoCancelState (void); - void setEchoCancelState (const std::string& state); std::string getNoiseSuppressState (void); void setNoiseSuppressState (const std::string& state); diff --git a/sflphone-common/src/eventthread.cpp b/sflphone-common/src/eventthread.cpp index cc16b5e77f45e448e13455b5ed99853c9b17fcda..a1aa2416e6b0b3f62d8791695699cb7e44620619 100644 --- a/sflphone-common/src/eventthread.cpp +++ b/sflphone-common/src/eventthread.cpp @@ -46,7 +46,8 @@ EventThread::EventThread (VoIPLink *link) void EventThread::run (void) { while (!testCancel()) { - _linkthread->getEvent(); + if (_linkthread) + _linkthread->getEvent(); } } diff --git a/sflphone-common/src/iax/iaxvoiplink.cpp b/sflphone-common/src/iax/iaxvoiplink.cpp index a9d4f117ea361c1fdba5731c715dcc57b9b2c77e..6679bbf4b783bdb4880ec9d39c5ceaaf71aa2e89 100644 --- a/sflphone-common/src/iax/iaxvoiplink.cpp +++ b/sflphone-common/src/iax/iaxvoiplink.cpp @@ -51,20 +51,26 @@ #define CHK_VALID_CALL if (call == NULL) { _debug("IAX: Call doesn't exists"); \ return false; } -IAXVoIPLink::IAXVoIPLink (const AccountID& accountID) - : VoIPLink (accountID) +IAXVoIPLink::IAXVoIPLink (const AccountID& accountID) : VoIPLink (accountID) + , _evThread (NULL) + , _regSession (NULL) + , _nextRefreshStamp (0) + , audiolayer (NULL) + , micData (NULL) + , micDataConverted (NULL) + , micDataEncoded (NULL) + , spkrDataDecoded (NULL) + , spkrDataConverted (NULL) + , converter (NULL) + , converterSamplingRate (NULL) + , urlhook (NULL) + , countTime (0) { - // _debug("IAXVoIPLink::IAXVoIPLink : creating eventhread "); _evThread = new EventThread (this); - _regSession = NULL; - _nextRefreshStamp = 0; - countTime = 0; // to get random number for RANDOM_PORT srand (time (NULL)); - audiolayer = NULL; - converter = new SamplerateConverter(); int nbSamplesMax = (int) (converter->getFrequence() * converter->getFramesize() / 1000); @@ -82,26 +88,46 @@ IAXVoIPLink::IAXVoIPLink (const AccountID& accountID) IAXVoIPLink::~IAXVoIPLink() { - delete _evThread; - _evThread = NULL; + if (_evThread) { + delete _evThread; + _evThread = NULL; + } + _regSession = NULL; // shall not delete it terminate(); audiolayer = NULL; - delete converter; + if (converter) { + delete converter; + converter = NULL; + } + + if (micData) { + delete [] micData; + micData = NULL; + } + + if (micDataConverted) { + delete [] micDataConverted; + micDataConverted = NULL; + } + + if (micDataEncoded) { + delete [] micDataEncoded; + micDataEncoded = NULL; + } + + if (spkrDataDecoded) { + delete [] spkrDataDecoded; + spkrDataDecoded = NULL; + } - delete [] micData; - micData = NULL; - delete [] micDataConverted; - micDataConverted = NULL; - delete [] micDataEncoded; - micDataEncoded = NULL; + if (spkrDataConverted) { + delete [] spkrDataConverted; + spkrDataConverted = NULL; + } - delete [] spkrDataDecoded; - spkrDataDecoded = NULL; - delete [] spkrDataConverted; - spkrDataConverted = NULL; } bool @@ -219,6 +245,8 @@ IAXVoIPLink::getEvent() { IAXCall* call = NULL; + Manager::instance().getAudioLayerMutex()->enter(); + // lock iax_ stuff.. _mutexIAX.enterMutex(); iax_event* event = NULL; @@ -254,11 +282,7 @@ IAXVoIPLink::getEvent() sendAudioFromMic(); - if (call) { - call->recAudio.recData (spkrDataDecoded, micData, nbSampleForRec_, nbSampleForRec_); - - // Do the doodle-moodle to send audio from the microphone to the IAX channel. - } + Manager::instance().getAudioLayerMutex()->leave(); // Do the doodle-moodle to send audio from the microphone to the IAX channel. // sendAudioFromMic(); @@ -495,7 +519,7 @@ IAXVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) if (call) { call->setPeerNumber (toUrl); - call->initRecFileName(); + call->initRecFileName (toUrl); if (iaxOutgoingInvite (call)) { call->setConnectionState (Call::Progressing); @@ -537,7 +561,7 @@ IAXVoIPLink::answer (const CallID& id) bool IAXVoIPLink::hangup (const CallID& id) { - _debug ("IAXVoIPLink::hangup() : function called once hangup "); + _debug ("IAXVoIPLink: Hangup"); IAXCall* call = getIAXCall (id); std::string reason = "Dumped Call"; CHK_VALID_CALL; @@ -565,7 +589,7 @@ IAXVoIPLink::hangup (const CallID& id) bool IAXVoIPLink::peerHungup (const CallID& id) { - _debug ("IAXVoIPLink::peerHangup() : function called once hangup "); + _debug ("IAXVoIPLink: Peer hung up"); IAXCall* call = getIAXCall (id); std::string reason = "Dumped Call"; CHK_VALID_CALL; @@ -681,6 +705,24 @@ IAXVoIPLink::carryingDTMFdigits (const CallID& id, char code) return true; } +bool +IAXVoIPLink::sendTextMessage (sfl::InstantMessaging *module, const std::string& callID, const std::string& message, const std::string& from) +{ + IAXCall* call = getIAXCall (callID); + + CHK_VALID_CALL; + + // Must active the mutex for this session + _mutexIAX.enterMutex(); + + module->send_iax_message (call->getSession(), callID, message.c_str()); + + // iax_send_text (call->getSession(), message.c_str()); + _mutexIAX.leaveMutex(); + + return true; +} + std::string IAXVoIPLink::getCurrentCodecName() @@ -870,6 +912,7 @@ IAXVoIPLink::iaxHandleCallEvent (iax_event* event, IAXCall* call) break; case IAX_EVENT_TEXT: + Manager::instance ().incomingMessage (call->getCallId (), call->getPeerNumber(), std::string ( (const char*) event->data)); break; case IAX_EVENT_RINGA: @@ -898,6 +941,37 @@ IAXVoIPLink::iaxHandleCallEvent (iax_event* event, IAXCall* call) break; case IAX_EVENT_TRANSFER: + _debug ("IAX_EVENT_TRANSFER"); + + if (call->getConnectionState() != Call::Connected) { + + Manager::instance().addStream (call->getCallId()); + + call->setConnectionState (Call::Connected); + call->setState (Call::Active); + // audiolayer->startStream(); + + _debug ("IAX_EVENT_ANSWER: codec format: "); + + if (event->ies.format) { + // Should not get here, should have been set in EVENT_ACCEPT + printf ("%i", event->ies.format); + call->setFormat (event->ies.format); + } + + { + printf ("no codec format"); + } + + Manager::instance().peerAnsweredCall (id); + + // start audio here? + audiolayer->startStream(); + audiolayer->flushMain(); + } else { + // deja connecté ? + } + break; default: @@ -1117,7 +1191,7 @@ IAXVoIPLink::iaxHandlePrecallEvent (iax_event* event) call->setPeerName (std::string (event->ies.calling_name)); // if peerNumber exist append it to the name string - call->initRecFileName(); + call->initRecFileName (std::string (event->ies.calling_number)); if (Manager::instance().incomingCall (call, getAccountID())) { /** @todo Faudra considérer éventuellement le champ CODEC PREFS pour diff --git a/sflphone-common/src/iax/iaxvoiplink.h b/sflphone-common/src/iax/iaxvoiplink.h index 4fe101b4d07029b51947dd8cda6c9273fbaf3e5c..d01436caf50b9c8fd508483e748d90d7a9664939 100644 --- a/sflphone-common/src/iax/iaxvoiplink.h +++ b/sflphone-common/src/iax/iaxvoiplink.h @@ -40,6 +40,8 @@ #include "audio/samplerateconverter.h" #include "hooks/urlhook.h" +#include "im/InstantMessaging.h" + class EventThread; class IAXCall; @@ -188,9 +190,8 @@ class IAXVoIPLink : public VoIPLink */ bool carryingDTMFdigits (const CallID& id, char code); - bool sendMessage (const std::string& to UNUSED, const std::string& body UNUSED) { - return false; - } + + bool sendTextMessage (sfl::InstantMessaging *module, const std::string& callID, const std::string& message, const std::string& from); bool isContactPresenceSupported() { return false; diff --git a/sflphone-common/src/im/InstantMessaging.cpp b/sflphone-common/src/im/InstantMessaging.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f927ab291ee73da85ecc372e34e4078640c3cf4 --- /dev/null +++ b/sflphone-common/src/im/InstantMessaging.cpp @@ -0,0 +1,399 @@ +#include "InstantMessaging.h" + +#include "expat.h" + +namespace sfl +{ + +static inline char* duplicateString (char dst[], const char src[], size_t len) +{ + memcpy (dst, src, len); + dst[len] = 0; + return dst; +} + +static void XMLCALL startElementCallback (void *userData, const char *name, const char **atts) +{ + + char attribute[100]; + char value[100]; + + const char **att; + + // _debug ("InstantMessaging: StartElement Callback: %s", name); + + if (strcmp (name, "entry") == 0) { + + sfl::InstantMessaging::UriList *list = static_cast<sfl::InstantMessaging::UriList *> (userData); + sfl::InstantMessaging::UriEntry entry = sfl::InstantMessaging::UriEntry(); + + for (att = atts; *att; att += 2) { + + const char **val = att+1; + + duplicateString (attribute, *att, strlen (*att)); + duplicateString (value, *val, strlen (*val)); + + // _debug ("InstantMessaging: attribute: %s, value: %s", attribute, value); + + entry.insert (std::pair<std::string, std::string> (std::string (attribute), std::string (value))); + } + + list->push_back (entry); + } + +} + +static void XMLCALL endElementCallback (void *userData, const char *name) +{ + // std::cout << "endElement " << name << std::endl; +} + + +InstantMessaging::InstantMessaging() + : imFiles () + , messageMaxSize (MAXIMUM_MESSAGE_LENGTH) {} + + +InstantMessaging::~InstantMessaging() {} + +bool InstantMessaging::init () +{ + return true; +} + +int InstantMessaging::openArchive (CallID& id) +{ + + // Create a new file stream + std::ofstream File (id.c_str (), std::ios::out | std::ios::app); + imFiles[id] = &File; + + // Attach it to the call ID + return (int) imFiles.size (); +} + +int InstantMessaging::closeArchive (CallID& id) +{ + + // Erase it from the map + imFiles.erase (id); + return (int) imFiles.size (); +} + +bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode) +{ + + // We need here to write the text message in the right file. + // We will use the Call ID + + std::ofstream File; + std::string filename = "im:"; + + filename.append (id); + File.open (filename.c_str (), (std::_Ios_Openmode) mode); + + if (!File.good () || !File.is_open ()) + return false; + + File << "[" << author << "] " << message << '\n'; + File.close (); + + return true; +} + +std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id) +{ + + // We just receive a TEXT message. Before sent it to the recipient, we must assure that the message is complete. + // We should use a queue to push these messages in + + _debug ("New message : %s", message.c_str ()); + + // TODO Security check + // TODO String cleaning + + // Archive the message + this->saveMessage (message, author, id); + + + return message; + +} + +pj_status_t InstantMessaging::notify (CallID& id) +{ + // Notify the clients through a D-Bus signal + return PJ_SUCCESS; +} + +pj_status_t InstantMessaging::sip_send (pjsip_inv_session *session, CallID& id, const std::string& text) +{ + + pjsip_method msg_method; + const pj_str_t type = STR_TEXT; + const pj_str_t subtype = STR_PLAIN; + pjsip_tx_data *tdata; + pj_status_t status; + pjsip_dialog* dialog; + pj_str_t message; + + msg_method.id = PJSIP_OTHER_METHOD; + msg_method.name = METHOD_NAME; + + // Get the dialog associated to the call + dialog = session->dlg; + // Convert the text into a format readable by pjsip + + message = pj_str ( (char*) text.c_str ()); + + // Must lock dialog + pjsip_dlg_inc_lock (dialog); + + // Create the message request + status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata); + PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); + + // Attach "text/plain" body + tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message); + + // Create the Require header to handle recipient-list Content-Disposition type + // pjsip_generic_string_hdr reqhdr; + // pj_str_t reqhname = pj_str ("Require"); + // pj_str_t reqhvalue = pj_str ("recipient-list"); + + // Create the Content-Type header to handle multipart/mixed and boundary MIME types + // pj_str_t ctype = pj_str ("Content-Type"); + // pj_str_t sctype = pj_str ("ctype"); // small version of the header name + // ctypehdr = pjsip_msg_find_hdr_by_names (tdata->msg, &ctype, &sctype, NULL); + // pjsip_generic_string_hdr ctypehdr; + // pj_str_t ctypehname = pj_str ("Content-Type"); + // pj_str_t ctypehvalue = pj_str ("multipart/mixed;boundary=\"boundary\""); + + // Add headers to the message + // pjsip_generic_string_hdr_init2 (&reqhdr, &reqhname, &reqhvalue); + // pj_list_push_back (& (tdata->msg->hdr), (pjsip_hdr*) (&reqhdr)); + // pj_list_push_back (& (tdata->msg->hdr), (pjsip_hdr*) (&ctypehdr)); + + // Send the request + status = pjsip_dlg_send_request (dialog, tdata, -1, NULL); + // PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); + + // Done + pjsip_dlg_dec_lock (dialog); + + // Archive the message + this->saveMessage (text, "Me", id); + + printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str()); + return PJ_SUCCESS; +} + +pj_status_t InstantMessaging::send_sip_message (pjsip_inv_session *session, CallID& id, const std::string& message) +{ + + /* Check the length of the message */ + if (message.length() < getMessageMaximumSize()) { + /* No problem here */ + sip_send (session, id, message); + } + + else { + /* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */ + std::vector<std::string> multiple_messages = split_message (message); + /* Send multiple messages */ + // int size = multiple_messages.size(); + int i = 0; + + // Maximum is above 1500 character + // TODO: Send every messages + sip_send (session, id, multiple_messages[i]); + } + + return PJ_SUCCESS; +} + + +bool InstantMessaging::iax_send (iax_session* session, const CallID& id, const std::string& message) +{ + if (iax_send_text (session, message.c_str()) != -1) + return true; + else + return false; + + +} + +bool InstantMessaging::send_iax_message (iax_session* session, const CallID& id, const std::string& message) +{ + + bool ret; + + /* Check the length of the message */ + if (message.length() < getMessageMaximumSize()) { + /* No problem here */ + ret = iax_send (session, id, message); + } + + else { + /* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */ + std::vector<std::string> multiple_messages = split_message (message); + /* Send multiple messages */ + // int size = multiple_messages.size(); + int i = 0; + + // Maximum is above 1500 character + // TODO: Send every messages + ret = iax_send (session, id, multiple_messages[i]); + } + + return ret; +} + + +std::vector<std::string> InstantMessaging::split_message (const std::string& text) +{ + + std::vector<std::string> messages; + std::string text_to_split = text; + + /* Iterate over the message length */ + while (text_to_split.length() > getMessageMaximumSize()) { + /* The remaining string is still too long */ + + /* Compute the substring */ + std::string split_message = text_to_split.substr (0, (size_t) getMessageMaximumSize()); + /* Append our split character \n\n */ + split_message.append (DELIMITER_CHAR); + /* Append in the vector */ + messages.push_back (split_message); + /* Use the remaining string to not loop forever */ + text_to_split = text_to_split.substr ( (size_t) getMessageMaximumSize()); + } + + /* Push the last message */ + /* If the message length does not exceed the maximum size of a SIP MESSAGE, we go directly here */ + messages.push_back (text_to_split); + + return messages; +} + +std::string InstantMessaging::generateXmlUriList (UriList& list) +{ + + std::string xmlbuffer = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + xmlbuffer.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"); + xmlbuffer.append ("<list>"); + + // An iterator over xml attribute + UriEntry::iterator iterAttr; + + // An iterator over list entries + UriList::iterator iterEntry = list.begin(); + + while (iterEntry != list.end()) { + xmlbuffer.append ("<entry uri="); + UriEntry entry = static_cast<UriEntry> (*iterEntry); + iterAttr = entry.find (sfl::IM_XML_URI); + xmlbuffer.append (iterAttr->second); + xmlbuffer.append (" cp:copyControl=\"to\" />"); + + iterEntry++; + } + + xmlbuffer.append ("</list>"); + xmlbuffer.append ("</resource-lists>"); + + return xmlbuffer; +} + + +InstantMessaging::UriList InstantMessaging::parseXmlUriList (std::string& urilist) +{ + InstantMessaging::UriList list; + + XML_Parser parser = XML_ParserCreate (NULL); + XML_SetUserData (parser, &list); + XML_SetElementHandler (parser, startElementCallback, endElementCallback); + + if (XML_Parse (parser, urilist.c_str(), urilist.size(), 1) == XML_STATUS_ERROR) { + std::cout << "Error: " << XML_ErrorString (XML_GetErrorCode (parser)) + << " at line " << XML_GetCurrentLineNumber (parser) << std::endl; + throw InstantMessageException ("Error while parsing uri-list xml content"); + } + + return list; +} + +std::string InstantMessaging::appendUriList (std::string text, UriList& list) +{ + + std::string formatedText = "--boundary Content-Type: text/plain"; + + formatedText.append (text); + formatedText.append ("--boundary Content-Type: application/resource-lists+xml"); + formatedText.append ("Content-Disposition: recipient-list"); + + std::string recipientlist = generateXmlUriList (list); + + formatedText.append (recipientlist); + + formatedText.append ("--boundary--"); + + return formatedText; +} + +std::string InstantMessaging::findTextUriList (std::string& text) +{ + std::string ctype = "Content-Type: application/resource-lists+xml"; + std::string cdispo = "Content-Disposition: recipient-list"; + std::string boundary = ("--boundary--"); + + // init position pointer + size_t pos = 0; + size_t begin = 0; + size_t end = 0; + + // find the content type + if ( (pos = text.find (ctype)) == std::string::npos) + throw InstantMessageException ("Could not find Content-Type tag while parsing sip message for recipient-list"); + + // find the content disposition + if ( (pos = text.find (cdispo, pos)) == std::string::npos) + throw InstantMessageException ("Could not find Content-Disposition tag while parsing sip message for recipient-list"); + + // xml content start after content disposition tag (plus \n\n) + begin = pos+cdispo.size(); + + // find final boundary + if ( (end = text.find (boundary, begin)) == std::string::npos) + throw InstantMessageException ("Could not find final \"boundary\" while parsing sip message for recipient-list"); + + return text.substr (begin, end-begin); +} + +std::string InstantMessaging::findTextMessage (std::string& text) +{ + std::string ctype = "Content-Type: text/plain"; + std::string boundary = "--boundary"; + + size_t pos = 0; + size_t begin = 0; + size_t end = 0; + + // find the content type + if ( (pos = text.find (ctype)) == std::string::npos) + throw InstantMessageException ("Could not find Content-Type tag while parsing sip message for text"); + + // plain text content start after content type tag (plus \n\n) + begin = pos+ctype.size(); + + // retrive end of the text content + if ( (end = text.find (boundary, begin)) == std::string::npos) + throw InstantMessageException ("Could not find end of text \"boundary\" while parsing sip message for text"); + + return text.substr (begin, end-begin); +} + + +} diff --git a/sflphone-common/src/sip/im/InstantMessaging.h b/sflphone-common/src/im/InstantMessaging.h similarity index 57% rename from sflphone-common/src/sip/im/InstantMessaging.h rename to sflphone-common/src/im/InstantMessaging.h index bcc25d85c11dbeb6577ea2d574e99fb7b78bacb1..3ba52df45c1b9ab394c1e2767ccff9dd979270e9 100644 --- a/sflphone-common/src/sip/im/InstantMessaging.h +++ b/sflphone-common/src/im/InstantMessaging.h @@ -12,6 +12,12 @@ #include "call.h" #include "sip/sipcall.h" +#include <map> +#include <list> +#include <exception> + +#include <iax-client.h> + #define EMPTY_MESSAGE pj_str((char*)"") #define STR_TEXT pj_str((char*)"text") #define STR_PLAIN pj_str((char*)"plain") @@ -25,9 +31,34 @@ namespace sfl { +const std::string IM_XML_URI ("uri"); +const std::string BOUNDARY ("--boundary"); + +class InstantMessageException : public std::exception +{ + public: + InstantMessageException (const std::string& str="") throw() : errstr (str) {} + + virtual ~InstantMessageException() throw() {} + + virtual const char *what() const throw() { + std::string expt ("InstantMessageException occured: "); + expt.append (errstr); + + return expt.c_str(); + } + private: + std::string errstr; +}; + class InstantMessaging { + public: + + typedef std::map <std::string, std::string> UriEntry; + typedef std::list <UriEntry> UriList; + /* * Class constructor */ @@ -100,9 +131,13 @@ class InstantMessaging * @return pj_status_t 0 on success * 1 otherwise */ - pj_status_t send (pjsip_inv_session*, CallID& id, const std::string&); + pj_status_t sip_send (pjsip_inv_session*, CallID& id, const std::string&); - pj_status_t send_message (pjsip_inv_session*, CallID& id, const std::string&); + pj_status_t send_sip_message (pjsip_inv_session*, CallID& id, const std::string&); + + bool iax_send (iax_session* session, const CallID& id, const std::string& message); + + bool send_iax_message (iax_session *session, const CallID& id, const std::string&); std::vector<std::string> split_message (const std::string&); @@ -114,6 +149,54 @@ class InstantMessaging */ pj_status_t notify (CallID& id); + + /** + * Generate Xml participant list for multi recipient based on RFC Draft 5365 + * + * @param A UriList of UriEntry + * + * @return A string containing the full XML formated information to be included in the + * sip instant message. + */ + std::string generateXmlUriList (UriList& list); + + /** + * Parse the Urilist from a SIP Instant Message provided by a UriList service. + * + * @param A XML formated string as obtained from a SIP instant message. + * + * @return An UriList of UriEntry containing parsed XML information as a map. + */ + UriList parseXmlUriList (std::string& urilist); + + /** + * Format text message according to RFC 5365, append recipient-list to the message + * + * @param text to be displayed + * @param list containing the recipients + * + * @return formated text stored into a string to be included in sip MESSAGE + */ + std::string appendUriList (std::string text, UriList& list); + + /** + * Retreive the xml formated uri list in formated text data according to RFC 5365 + * + * @param text The formated text message as retreived in the SIP message + * + * @return A string containing the XML content + */ + std::string findTextUriList (std::string& text); + + /** + * Retrive the plain text message in formated text data according to RFC 5365 + * + * @param text The formated text message as retreived in the SIP message + * + * @return A string containing the actual message + */ + std::string findTextMessage (std::string& text); + private: /** diff --git a/sflphone-common/src/sip/im/Makefile.am b/sflphone-common/src/im/Makefile.am similarity index 100% rename from sflphone-common/src/sip/im/Makefile.am rename to sflphone-common/src/im/Makefile.am diff --git a/sflphone-common/src/managerimpl.cpp b/sflphone-common/src/managerimpl.cpp index ccdfa03f8afdb004de41d531bbec9753dc703084..8bbc44abbce67a2dde7a19e3952ec0d170913136 100644 --- a/sflphone-common/src/managerimpl.cpp +++ b/sflphone-common/src/managerimpl.cpp @@ -48,6 +48,7 @@ #include "history/historymanager.h" #include "accountcreator.h" // create new account #include "sip/sipvoiplink.h" +#include "iax/iaxvoiplink.h" #include "manager.h" #include "dbus/configurationmanager.h" @@ -92,6 +93,7 @@ ManagerImpl::ManagerImpl (void) : _cleaner = new NumberCleaner(); _history = new HistoryManager(); + _imModule = new sfl::InstantMessaging(); #ifdef TEST testAccountMap(); @@ -117,6 +119,8 @@ ManagerImpl::~ManagerImpl (void) _cleaner = NULL; delete _history; _history = NULL; + delete _imModule; + _imModule = NULL; _debug ("Manager: %s stop correctly.", PROGNAME); } @@ -159,6 +163,11 @@ void ManagerImpl::init () // Load the history _history->load_history (preferences.getHistoryLimit()); + + // Init the instant messaging module + _imModule->init(); + + } void ManagerImpl::terminate () @@ -1712,7 +1721,6 @@ void ManagerImpl::incomingMessage (const CallID& callID, const std::string& from, const std::string& message) { - SIPVoIPLink *link = NULL; if (participToConference (callID)) { _debug ("Manager: Particip to a conference, send message to everyone"); @@ -1724,38 +1732,104 @@ void ManagerImpl::incomingMessage (const CallID& callID, while (iter_participant != participants.end()) { + if (*iter_participant == callID) + continue; + AccountID accountId = getAccountFromCall (*iter_participant); _debug ("Manager: Send message to %s, (%s)", (*iter_participant).c_str(), accountId.c_str()); - if (*iter_participant != callID) { + Account *account = getAccount (accountId); - link = SIPVoIPLink::instance (""); // dynamic_cast<SIPVoIPLink *> (getAccountLink (*iter_participant)); + if (!account) { + _debug ("Manager: Failed to get account while sending instant message"); + return; + } - if (link) - link->sendTextMessage (*iter_participant, message); + if (account->getType() == "SIP") + // link = dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId)); + dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId))->sendTextMessage (_imModule, callID, message, from); + else if (account->getType() == "IAX") + // link = dynamic_cast<IAXVoIPLink *> (account->getVoIPLink()); + dynamic_cast<IAXVoIPLink *> (account->getVoIPLink())->sendTextMessage (_imModule, callID, message, from); + else { + _debug ("Manager: Failed to get voip link while sending instant message"); + return; } iter_participant++; } - } - if (_dbus) { - _dbus->getCallManager()->incomingMessage (callID, from, message); + // in case of a conference we must notify client using conference id + if (_dbus) { + _dbus->getCallManager()->incomingMessage (conf->getConfID(), from, message); + } + + } else { + + if (_dbus) { + _dbus->getCallManager()->incomingMessage (callID, from, message); + } } } //THREAD=VoIP -bool ManagerImpl::sendTextMessage (const CallID& callID, const std::string& message) +bool ManagerImpl::sendTextMessage (const CallID& callID, const std::string& message, const std::string& from) { - SIPVoIPLink * link = NULL; + + if (isConference (callID)) { + _debug ("Manager: Is a conference, send instant message to everyone"); + + ConferenceMap::iterator it = _conferencemap.find (callID); + + if (it == _conferencemap.end()) + return false; + + Conference *conf = it->second; + + if (!conf) + return false; + + ParticipantSet participants = conf->getParticipantList(); + ParticipantSet::iterator iter_participant = participants.begin(); + + while (iter_participant != participants.end()) { + + AccountID accountId = getAccountFromCall (*iter_participant); + + Account *account = getAccount (accountId); + + if (!account) { + _debug ("Manager: Failed to get account while sending instant message"); + return false; + } + + if (account->getType() == "SIP") + // link = dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId)); + dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId))->sendTextMessage (_imModule, *iter_participant, message, from); + else if (account->getType() == "IAX") + // link = dynamic_cast<IAXVoIPLink *> (account->getVoIPLink()); + dynamic_cast<IAXVoIPLink *> (account->getVoIPLink())->sendTextMessage (_imModule, *iter_participant, message, from); + else { + _debug ("Manager: Failed to get voip link while sending instant message"); + return false; + } + + iter_participant++; + } + + return true; + } if (participToConference (callID)) { - _debug ("Manager: Particip to a conference, send message on everyone"); + _debug ("Manager: Particip to a conference, send instant message to everyone"); Conference *conf = getConferenceFromCallID (callID); + if (!conf) + return false; + ParticipantSet participants = conf->getParticipantList(); ParticipantSet::iterator iter_participant = participants.begin(); @@ -1763,12 +1837,23 @@ bool ManagerImpl::sendTextMessage (const CallID& callID, const std::string& mess AccountID accountId = getAccountFromCall (*iter_participant); - _debug ("Manager: Send message to %s (%s)", (*iter_participant).c_str(), accountId.c_str()); - link = SIPVoIPLink::instance (""); // dynamic_cast<SIPVoIPLink *> (getAccountLink (*iter_participant)); + Account *account = getAccount (accountId); + if (!account) { + _debug ("Manager: Failed to get account while sending instant message"); + return false; + } - if (link) - link->sendTextMessage (*iter_participant, message); + if (account->getType() == "SIP") + // link = dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId)); + dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId))->sendTextMessage (_imModule, *iter_participant, message, from); + else if (account->getType() == "IAX") + // link = dynamic_cast<IAXVoIPLink *> (account->getVoIPLink()); + dynamic_cast<IAXVoIPLink *> (account->getVoIPLink())->sendTextMessage (_imModule, *iter_participant, message, from); + else { + _debug ("Manager: Failed to get voip link while sending instant message"); + return false; + } iter_participant++; } @@ -1777,15 +1862,23 @@ bool ManagerImpl::sendTextMessage (const CallID& callID, const std::string& mess AccountID accountId = getAccountFromCall (callID); - link = dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId)); + Account *account = getAccount (accountId); - if (link == NULL) { - _debug ("Manager: Failed to get sip link"); + if (!account) { + _debug ("Manager: Failed to get account while sending instant message"); return false; } - _debug ("Manager: Send message to %s (%s)", callID.c_str(), accountId.c_str()); - link->sendTextMessage (callID, message); + if (account->getType() == "SIP") + // link = dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId)); + dynamic_cast<SIPVoIPLink *> (getAccountLink (accountId))->sendTextMessage (_imModule, callID, message, from); + else if (account->getType() == "IAX") + // link = dynamic_cast<IAXVoIPLink *> (account->getVoIPLink()); + dynamic_cast<IAXVoIPLink *> (account->getVoIPLink())->sendTextMessage (_imModule, callID, message, from); + else { + _debug ("Manager: Failed to get voip link while sending instant message"); + return false; + } } return true; @@ -2733,30 +2826,6 @@ std::string ManagerImpl::getCurrentAudioOutputPlugin (void) } -std::string ManagerImpl::getEchoCancelState (void) -{ - - std::string state; - - state = audioPreference.getEchoCancel() ? "enabled" : "disabled"; - - return state; -} - -void ManagerImpl::setEchoCancelState (std::string state) -{ - _debug ("Manager: Set echo suppress state: %s", state.c_str()); - - bool isEnabled = state == "enabled" ? true : false; - - audioPreference.setEchoCancel (isEnabled); - - if (_audiodriver) { - _audiodriver->setEchoCancelState (isEnabled); - } -} - - std::string ManagerImpl::getNoiseSuppressState (void) { @@ -3790,7 +3859,7 @@ void ManagerImpl::unloadAccountMap () while (iter != _accountMap.end()) { - _debug ("Unloading account %s", iter->first.c_str()); + _debug ("Manager: Unloading account %s", iter->first.c_str()); delete iter->second; iter->second = NULL; diff --git a/sflphone-common/src/managerimpl.h b/sflphone-common/src/managerimpl.h index db6c91ccf705d10148ed118d493cbbd8ac1c5286..2e2c729b804ed8829df7af63cdc4db059069c59c 100644 --- a/sflphone-common/src/managerimpl.h +++ b/sflphone-common/src/managerimpl.h @@ -59,6 +59,8 @@ #include "yamlparser.h" #include "preferences.h" +#include "im/InstantMessaging.h" + class AudioLayer; class GuiFramework; class TelephoneTone; @@ -448,11 +450,12 @@ class ManagerImpl /** - * Send a new text message to the call, if participate to a conference, send to all participant. + * Send a new text message to the call, if participate to a conference, send to all participant. * @param callID The call to send the message * @param message The content of the message - */ - bool sendTextMessage (const CallID& callID, const std::string& message); + * @param from The sender of this message (could be another participant of a conference) + */ + bool sendTextMessage (const CallID& callID, const std::string& message, const std::string& from); /** * Notify the client he has voice mails @@ -616,12 +619,16 @@ class ManagerImpl */ std::string getCurrentAudioOutputPlugin (void); - std::string getEchoCancelState (void); - - void setEchoCancelState (std::string state); - + /** + * Get the noise reduction engin state from + * the current audio layer. + */ std::string getNoiseSuppressState (void); + /** + * Set the noise reduction engin state in the current + * audio layer. + */ void setNoiseSuppressState (std::string state); /** @@ -1308,6 +1315,13 @@ class ManagerImpl MainBuffer _mainBuffer; + /** + * Instant messaging module, resposible to initiate, format, parse, + * send, and receive instant messages. + */ + sfl::InstantMessaging *_imModule; + + public: /** @@ -1317,6 +1331,13 @@ class ManagerImpl return &_mainBuffer; } + /** + * Return a pointer to the instance of InstantMessaging + */ + sfl::InstantMessaging *getInstantMessageModule (void) { + return _imModule; + } + /** * Tell if there is a current call processed diff --git a/sflphone-common/src/preferences.cpp b/sflphone-common/src/preferences.cpp index 310e214e7f51a7002455abc4868d7ba05781a24d..2354ad15bc62c0b0414c8ea1ecb46d8e907ed5a0 100644 --- a/sflphone-common/src/preferences.cpp +++ b/sflphone-common/src/preferences.cpp @@ -34,7 +34,7 @@ #include "user_cfg.h" Preferences::Preferences() : _accountOrder ("") - , _audioApi (0) + , _audioApi (1) // 1 is pulseaudio, 0 alsa , _historyLimit (30) , _historyMaxCalls (20) , _notifyMails (false) @@ -59,9 +59,9 @@ void Preferences::serialize (Conf::YamlEmitter *emiter) Conf::MappingNode preferencemap (NULL); Conf::ScalarNode order (_accountOrder); - std::stringstream audiostr; - audiostr << _audioApi; - Conf::ScalarNode audioapi (audiostr.str()); + // std::stringstream audiostr; + // audiostr << _audioApi; + Conf::ScalarNode audioapi (_audioApi == 1 ? "pulseaudio" : "alsa"); std::stringstream histlimitstr; histlimitstr << _historyLimit; Conf::ScalarNode historyLimit (histlimitstr.str()); @@ -115,7 +115,8 @@ void Preferences::unserialize (Conf::MappingNode *map) val = (Conf::ScalarNode *) (map->getValue (audioApiKey)); if (val) { - _audioApi = atoi (val->getValue().data()); + // 1 is pulseaudio, 0 is alsa + _audioApi = (val->getValue().compare ("pulseaudio") == 0) ? 1 : 0; val = NULL; } @@ -190,7 +191,6 @@ void Preferences::unserialize (Conf::MappingNode *map) VoipPreference::VoipPreference() : _playDtmf (true) , _playTones (true) , _pulseLength (atoi (DFT_PULSE_LENGTH_STR)) // DFT_PULSE_LENGTH_STR - , _sendDtmfAs (0) , _symmetricRtp (true) , _zidFile (ZRTP_ZIDFILE) // ZRTP_ZID_FILENAME { @@ -211,16 +211,12 @@ void VoipPreference::serialize (Conf::YamlEmitter *emitter) std::stringstream pulselengthstr; pulselengthstr << _pulseLength; Conf::ScalarNode pulseLength (pulselengthstr.str()); - std::stringstream senddtmfstr; - senddtmfstr << _sendDtmfAs; - Conf::ScalarNode sendDtmfAs (senddtmfstr.str()); Conf::ScalarNode symmetricRtp (_symmetricRtp ? "true" : "false"); Conf::ScalarNode zidFile (_zidFile.c_str()); preferencemap.setKeyValue (playDtmfKey, &playDtmf); preferencemap.setKeyValue (playTonesKey, &playTones); preferencemap.setKeyValue (pulseLengthKey, &pulseLength); - preferencemap.setKeyValue (sendDtmfAsKey, &sendDtmfAs); preferencemap.setKeyValue (symmetricRtpKey, &symmetricRtp); preferencemap.setKeyValue (zidFileKey, &zidFile); @@ -258,13 +254,6 @@ void VoipPreference::unserialize (Conf::MappingNode *map) val = NULL; } - val = (Conf::ScalarNode *) (map->getValue (sendDtmfAsKey)); - - if (val) { - _sendDtmfAs = atoi (val->getValue().data()); - val = NULL; - } - val = (Conf::ScalarNode *) (map->getValue (symmetricRtpKey)); if (val && !val->getValue().empty()) { @@ -489,7 +478,6 @@ AudioPreference::AudioPreference() : _cardin (atoi (ALSA_DFT_CARD)) // ALSA_DFT_ , _volumemic (atoi (DFT_VOL_SPKR_STR)) // DFT_VOL_SPKR_STR , _volumespkr (atoi (DFT_VOL_MICRO_STR)) // DFT_VOL_MICRO_STR , _noisereduce (true) - , _echocancel (true) { } @@ -536,8 +524,6 @@ void AudioPreference::serialize (Conf::YamlEmitter *emitter) spkrstr << _volumespkr; Conf::ScalarNode volumespkr (spkrstr.str()); //: 100 Conf::ScalarNode noise (_noisereduce ? "true":"false"); - Conf::ScalarNode echo (_echocancel ? "true":"false"); - preferencemap.setKeyValue (recordpathKey, &recordpath); preferencemap.setKeyValue (volumemicKey, &volumemic); preferencemap.setKeyValue (volumespkrKey, &volumespkr); @@ -556,7 +542,6 @@ void AudioPreference::serialize (Conf::YamlEmitter *emitter) pulsepreferencemap.setKeyValue (deviceRingtoneKey, &deviceRingtone); preferencemap.setKeyValue (noiseReduceKey, &noise); - preferencemap.setKeyValue (echocancelKey, &echo); emitter->serializeAudioPreference (&preferencemap); @@ -603,13 +588,6 @@ void AudioPreference::unserialize (Conf::MappingNode *map) val = NULL; } - val = (Conf::ScalarNode *) (map->getValue (echocancelKey)); - - if (val) { - _echocancel = (val->getValue() == "true"); - val = NULL; - } - alsamap = (Conf::MappingNode *) (map->getValue ("alsa")); // did found alsa diff --git a/sflphone-common/src/preferences.h b/sflphone-common/src/preferences.h index eb22c6cc3a1022bac105ac77f4534e058fb2003d..7a5c78cb5ff4c28e659f15da43612cb1bcfea4c7 100644 --- a/sflphone-common/src/preferences.h +++ b/sflphone-common/src/preferences.h @@ -50,7 +50,6 @@ const Conf::Key md5HashKey ("md5Hash"); // : false const Conf::Key playDtmfKey ("playDtmf"); // true true const Conf::Key playTonesKey ("playTones"); // true const Conf::Key pulseLengthKey ("pulseLength"); //=250 -const Conf::Key sendDtmfAsKey ("sendDtmfAs");// =0 const Conf::Key symmetricRtpKey ("symmetric");// =true const Conf::Key zidFileKey ("zidFile");// =sfl.zid @@ -74,21 +73,19 @@ const Conf::Key urlSipFieldKey ("urlSipField"); //: X-sflphone-url // audio preferences const Conf::Key alsamapKey ("alsa"); const Conf::Key pulsemapKey ("pulse"); -const Conf::Key cardinKey ("cardin");// : 0 -const Conf::Key cardoutKey ("cardout");// 0 -const Conf::Key cardringKey ("cardring");// : 0 -const Conf::Key framesizeKey ("framesize");// : 20 +const Conf::Key cardinKey ("cardIn");// : 0 +const Conf::Key cardoutKey ("cardOut");// 0 +const Conf::Key cardringKey ("cardRing");// : 0 +const Conf::Key framesizeKey ("frameSize");// : 20 const Conf::Key pluginKey ("plugin"); //: default -const Conf::Key smplrateKey ("smplrate");//: 44100 +const Conf::Key smplrateKey ("smplRate");//: 44100 const Conf::Key devicePlaybackKey ("devicePlayback");//: const Conf::Key deviceRecordKey ("deviceRecord");// : const Conf::Key deviceRingtoneKey ("deviceRingtone");// : -const Conf::Key recordpathKey ("recordpath");//: /home/msavard/Bureau -const Conf::Key volumemicKey ("volumemic");//: 100 -const Conf::Key volumespkrKey ("volumespkr");//: 100 +const Conf::Key recordpathKey ("recordPath");//: /home/msavard/Bureau +const Conf::Key volumemicKey ("volumeMic");//: 100 +const Conf::Key volumespkrKey ("volumeSpkr");//: 100 const Conf::Key noiseReduceKey ("noiseReduce"); -const Conf::Key echocancelKey ("echocancel"); - // shortcut preferences const Conf::Key hangupShortKey ("hangUp"); @@ -242,13 +239,6 @@ class VoipPreference : public Serializable _pulseLength = length; } - int getSendDtmfAs (void) { - return _sendDtmfAs; - } - void setSendDtmfAs (int dtmf) { - _sendDtmfAs = dtmf; - } - bool getSymmetricRtp (void) { return _symmetricRtp; } @@ -268,7 +258,6 @@ class VoipPreference : public Serializable bool _playDtmf; bool _playTones; int _pulseLength; - int _sendDtmfAs; bool _symmetricRtp; std::string _zidFile; @@ -524,14 +513,6 @@ class AudioPreference : public Serializable _noisereduce = noise; } - bool getEchoCancel (void) { - return _echocancel; - } - - void setEchoCancel (bool echo) { - _echocancel = echo; - } - private: // alsa preference @@ -553,7 +534,6 @@ class AudioPreference : public Serializable int _volumespkr; //: 100 bool _noisereduce; - bool _echocancel; }; diff --git a/sflphone-common/src/sip/Makefile.am b/sflphone-common/src/sip/Makefile.am index 9043a15a5badd977412259d78ce0bcc3cf2e320e..c20fea5c288d339dded2e64d178dfd2ad5896e68 100644 --- a/sflphone-common/src/sip/Makefile.am +++ b/sflphone-common/src/sip/Makefile.am @@ -1,5 +1,3 @@ -SUBDIRS=im - include $(top_srcdir)/globals.mak noinst_LTLIBRARIES = libsiplink.la @@ -13,8 +11,6 @@ libsiplink_la_SOURCES = \ sipcall.cpp \ sipvoiplink.cpp -libsiplink_la_LIBADD = im/libim.la - noinst_HEADERS = \ Pattern.h \ SdesNegotiator.h \ diff --git a/sflphone-common/src/sip/im/InstantMessaging.cpp b/sflphone-common/src/sip/im/InstantMessaging.cpp deleted file mode 100644 index d44acfdff140df5b451d8c2f8c6736edfa1690a0..0000000000000000000000000000000000000000 --- a/sflphone-common/src/sip/im/InstantMessaging.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "InstantMessaging.h" - -namespace sfl -{ - -InstantMessaging::InstantMessaging() - : imFiles () - , messageMaxSize (MAXIMUM_MESSAGE_LENGTH) {} - - -InstantMessaging::~InstantMessaging() {} - -bool InstantMessaging::init () -{ - return true; -} - -int InstantMessaging::openArchive (CallID& id) -{ - - // Create a new file stream - std::ofstream File (id.c_str (), std::ios::out | std::ios::app); - imFiles[id] = &File; - - // Attach it to the call ID - return (int) imFiles.size (); -} - -int InstantMessaging::closeArchive (CallID& id) -{ - - // Erase it from the map - imFiles.erase (id); - return (int) imFiles.size (); -} - -bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode) -{ - - // We need here to write the text message in the right file. - // We will use the Call ID - - std::ofstream File; - std::string filename = "im:"; - - filename.append (id); - File.open (filename.c_str (), (std::_Ios_Openmode) mode); - - if (!File.good () || !File.is_open ()) - return false; - - File << "[" << author << "] " << message << '\n'; - File.close (); - - return true; -} - -std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id) -{ - - // We just receive a TEXT message. Before sent it to the recipient, we must assure that the message is complete. - // We should use a queue to push these messages in - - _debug ("New message : %s", message.c_str ()); - - // TODO Security check - // TODO String cleaning - - // Archive the message - this->saveMessage (message, author, id); - - - return message; - -} - -pj_status_t InstantMessaging::notify (CallID& id) -{ - // Notify the clients through a D-Bus signal - return PJ_SUCCESS; -} - -pj_status_t InstantMessaging::send (pjsip_inv_session *session, CallID& id, const std::string& text) -{ - - pjsip_method msg_method; - const pj_str_t type = STR_TEXT; - const pj_str_t subtype = STR_PLAIN; - pjsip_tx_data *tdata; - pj_status_t status; - pjsip_dialog* dialog; - pj_str_t message; - - msg_method.id = PJSIP_OTHER_METHOD; - msg_method.name = METHOD_NAME ; - - - // Get the dialog associated to the call - dialog = session->dlg; - // Convert the text into a format readable by pjsip - message = pj_str ( (char*) text.c_str ()); - - // Must lock dialog - pjsip_dlg_inc_lock (dialog); - - // Create the message request - status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata); - PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); - - // Attach "text/plain" body - tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message); - - // Send the request - status = pjsip_dlg_send_request (dialog, tdata, -1, NULL); - // PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); - - // Done - pjsip_dlg_dec_lock (dialog); - - // Archive the message - this->saveMessage (text, "Me", id); - - printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str()); - return PJ_SUCCESS; -} - -pj_status_t InstantMessaging::send_message (pjsip_inv_session *session, CallID& id, const std::string& message) -{ - - /* Check the length of the message */ - if (message.length() < getMessageMaximumSize()) { - /* No problem here */ - send (session, id, message); - } - - else { - /* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */ - std::vector<std::string> multiple_messages = split_message (message); - /* Send multiple messages */ - int size = multiple_messages.size(); - int i = 0; - - // Maximum is above 1500 character - // TODO: Send every messages - send (session, id, multiple_messages[i]); - } - - return PJ_SUCCESS; -} - - -std::vector<std::string> InstantMessaging::split_message (const std::string& text) -{ - - std::vector<std::string> messages; - std::string text_to_split = text; - - /* Iterate over the message length */ - while (text_to_split.length() > getMessageMaximumSize()) { - /* The remaining string is still too long */ - - /* Compute the substring */ - std::string split_message = text_to_split.substr (0, (size_t) getMessageMaximumSize()); - /* Append our split character \n\n */ - split_message.append (DELIMITER_CHAR); - /* Append in the vector */ - messages.push_back (split_message); - /* Use the remaining string to not loop forever */ - text_to_split = text_to_split.substr ( (size_t) getMessageMaximumSize()); - } - - /* Push the last message */ - /* If the message length does not exceed the maximum size of a SIP MESSAGE, we go directly here */ - messages.push_back (text_to_split); - - return messages; -} -} diff --git a/sflphone-common/src/sip/sipaccount.cpp b/sflphone-common/src/sip/sipaccount.cpp index 0a31e1f98a9367a12afe5828582bfc945eef1954..7bba0b5b63a85c6026fe7b75d71aa8a5cf26cfc7 100644 --- a/sflphone-common/src/sip/sipaccount.cpp +++ b/sflphone-common/src/sip/sipaccount.cpp @@ -98,7 +98,7 @@ SIPAccount::SIPAccount (const AccountID& accountID) , _realm (DEFAULT_REALM) , _authenticationUsername ("") , _tlsSetting (NULL) - , _dtmfType (OVERRTP) + , _dtmfType (SIPINFO) , _tlsEnable ("false") , _tlsPortStr (DEFAULT_SIP_TLS_PORT) , _tlsCaListFile ("") @@ -190,7 +190,7 @@ void SIPAccount::serialize (Conf::YamlEmitter *emitter) Conf::ScalarNode stunServer (_stunServer); Conf::ScalarNode stunEnabled (_stunEnabled ? "true" : "false"); Conf::ScalarNode displayName (_displayName); - Conf::ScalarNode dtmfType (_dtmfType==0 ? "overrtp" : "sipinfo"); + Conf::ScalarNode dtmfType (_dtmfType==OVERRTP ? "overrtp" : "sipinfo"); std::stringstream countstr; countstr << 0; @@ -415,6 +415,7 @@ void SIPAccount::unserialize (Conf::MappingNode *map) val = (Conf::ScalarNode *) (map->getValue (dtmfTypeKey)); if (val) { + _dtmfType = (val->getValue() == "overrtp") ? OVERRTP : SIPINFO; val = NULL; } @@ -423,6 +424,7 @@ void SIPAccount::unserialize (Conf::MappingNode *map) if (val) { _serviceRoute = val->getValue(); + val = NULL; } // stun enabled @@ -723,6 +725,8 @@ void SIPAccount::setAccountDetails (const std::map<std::string, std::string>& de setPublishedPort (atoi (publishedPort.data())); setStunServer (stunServer); setStunEnabled ( (stunEnable == "true")); + setDtmfType ( (dtmfType == "overrtp") ? OVERRTP : SIPINFO); + setResolveOnce ( (resolveOnce.compare ("true") ==0) ? true : false); setRegistrationExpire (registrationExpire); @@ -879,7 +883,7 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() a.insert (std::pair<std::string, std::string> (PUBLISHED_PORT, publishedport.str())); a.insert (std::pair<std::string, std::string> (STUN_ENABLE, isStunEnabled() ? "true" : "false")); a.insert (std::pair<std::string, std::string> (STUN_SERVER, getStunServer())); - a.insert (std::pair<std::string, std::string> (ACCOUNT_DTMF_TYPE, (getDtmfType() == 0) ? "0" : "1")); + a.insert (std::pair<std::string, std::string> (ACCOUNT_DTMF_TYPE, (getDtmfType() == OVERRTP) ? "overrtp" : "sipinfo")); a.insert (std::pair<std::string, std::string> (SRTP_KEY_EXCHANGE, getSrtpKeyExchange())); a.insert (std::pair<std::string, std::string> (SRTP_ENABLE, getSrtpEnable() ? "true" : "false")); diff --git a/sflphone-common/src/sip/sipvoiplink.cpp b/sflphone-common/src/sip/sipvoiplink.cpp index a9cb6ff1e4163c85bfbafa8174eab26e31b9f8e3..134256a7919e87d1471e219ac5fa7cf76a5d0bfe 100644 --- a/sflphone-common/src/sip/sipvoiplink.cpp +++ b/sflphone-common/src/sip/sipvoiplink.cpp @@ -45,6 +45,8 @@ #include "dbus/dbusmanager.h" #include "dbus/callmanager.h" +#include "im/InstantMessaging.h" + #include "pjsip/sip_endpoint.h" #include "pjsip/sip_transport_tls.h" #include "pjsip/sip_transport_tls.h" @@ -165,11 +167,6 @@ pj_thread_desc desc; */ UrlHook *urlhook; -/* - * Instant Messaging module - */ -InstantMessaging *imModule; - /** * Get the number of voicemail waiting in a SIP message */ @@ -280,9 +277,6 @@ SIPVoIPLink::SIPVoIPLink (const AccountID& accountID) urlhook = new UrlHook (); - // Load the chat module - imModule = new InstantMessaging (); - /* Start pjsip initialization step */ init(); } @@ -830,7 +824,8 @@ SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) _error ("UserAgent: Error: Failed to create rtp thread from newOutGoingCall"); } - call->initRecFileName(); + // init file name according to peer phone number + call->initRecFileName (toUrl); _debug ("UserAgent: Try to make a call to: %s with call ID: %s", toUrl.data(), id.data()); @@ -1062,17 +1057,33 @@ SIPVoIPLink::onhold (const CallID& id) } bool -SIPVoIPLink::sendTextMessage (const std::string& callID, const std::string& message) +SIPVoIPLink::sendTextMessage (sfl::InstantMessaging *module, const std::string& callID, const std::string& message, const std::string& from) { - _debug ("SipVoipLink: Send text message to %s", callID.c_str()); + _debug ("SipVoipLink: Send text message to %s, from %s", callID.c_str(), from.c_str()); SIPCall *call = getSIPCall (callID); pj_status_t status = !PJ_SUCCESS; if (call) { + std::string formatedFrom = from; + + // add double quotes for xml formating + formatedFrom.insert (0,"\""); + formatedFrom.append ("\""); + /* Send IM message */ - status = imModule->send_message (call->getInvSession (), (CallID&) callID, message); + sfl::InstantMessaging::UriList list; + + sfl::InstantMessaging::UriEntry entry; + entry[sfl::IM_XML_URI] = std::string (formatedFrom); + + list.push_front (entry); + + std::string formatedMessage = module->appendUriList (message, list); + + status = module->send_sip_message (call->getInvSession (), (CallID&) callID, formatedMessage); + } else { /* Notify the client of an error */ /*Manager::instance ().incomingMessage ( "", @@ -1679,7 +1690,9 @@ bool SIPVoIPLink::new_ip_to_ip_call (const CallID& id, const std::string& to) if (call) { call->setCallConfiguration (Call::IPtoIP); - call->initRecFileName(); + + // Init recfile name using to uri + call->initRecFileName (to); SIPAccount * account = NULL; account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (IP2IP_PROFILE)); @@ -2081,9 +2094,6 @@ bool SIPVoIPLink::pjsip_init() const pj_str_t STR_MIME_TEXT_PLAIN = { (char*) "text/plain", 10 }; pjsip_endpt_add_capability (_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &STR_MIME_TEXT_PLAIN); - // Registering and initializing IM module - imModule->init (); - // Register "application/sdp" in ACCEPT header pjsip_endpt_add_capability (_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted); @@ -3103,6 +3113,25 @@ void call_on_state_changed (pjsip_inv_session *inv, pjsip_event *e) return; } + /* + pjsip_hdr *allow_header = NULL; + std::string *allowed_options = NULL; + + char header_buffer[500]; + + if (e->body.tsx_state.src.rdata->msg_info.msg) + allow_header = (pjsip_hdr *) pjsip_msg_find_hdr (e->body.tsx_state.src.rdata->msg_info.msg, PJSIP_H_ALLOW, NULL); + + if (allow_header) { + allowed_options = new std::string (allow_header->name.ptr, allow_header->name.slen); + allow_header->vptr->print_on (allow_header, header_buffer, 5000); + std::string theHeader (header_buffer); + } + + if (allowed_options) + delete allowed_options; + */ + // 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) { @@ -3441,15 +3470,15 @@ void call_on_tsx_changed (pjsip_inv_session *inv UNUSED, pjsip_transaction *tsx, // Incoming TEXT message if (e && e->body.tsx_state.src.rdata) { - std::string message; - SIPCall * call; + // sender of this message + std::string from; // Get the message inside the transaction r_data = e->body.tsx_state.src.rdata; - message = (char*) r_data->msg_info.msg->body->data; + std::string formatedMessage = (char*) r_data->msg_info.msg->body->data; // Try to determine who is the recipient of the message - call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId() ]); + SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId() ]); if (!call) { _debug ("Incoming TEXT message: Can't find the recipient of the message"); @@ -3460,12 +3489,55 @@ void call_on_tsx_changed (pjsip_inv_session *inv UNUSED, pjsip_transaction *tsx, pjsip_dlg_create_response (inv->dlg, r_data, PJSIP_SC_OK, NULL, &t_data); pjsip_dlg_send_response (inv->dlg, tsx, t_data); + std::string message; + std::string urilist; + sfl::InstantMessaging::UriList list; + + sfl::InstantMessaging *module = Manager::instance().getInstantMessageModule(); + + try { + // retrive message from formated text + message = module->findTextMessage (formatedMessage); + + // retreive the recipient-list of this message + urilist = module->findTextUriList (formatedMessage); + + // parse the recipient list xml + list = module->parseXmlUriList (urilist); + + // If no item present in the list, peer is considered as the sender + if (list.empty()) { + from = call->getPeerNumber (); + } else { + sfl::InstantMessaging::UriEntry entry = list.front(); + sfl::InstantMessaging::UriEntry::iterator iterAttr = entry.find (IM_XML_URI); + + if (iterAttr->second != "Me") + from = iterAttr->second; + else + from = call->getPeerNumber (); + } + + } catch (sfl::InstantMessageException &e) { + _error ("SipVoipLink: %s", e.what()); + message = ""; + from = call->getPeerNumber (); + } + + + // strip < and > characters in case of an IP address + std::string stripped; + + if (from[0] == '<' && from[from.size()-1] == '>') + stripped = from.substr (1, from.size()-2); + else + stripped = from; + // Pass through the instant messaging module if needed // Right now, it does do anything. // And notify the clients - Manager::instance ().incomingMessage (call->getCallId (), - call->getPeerNumber (), - imModule->receive (message, call->getPeerNumber (), call->getCallId ())); + + Manager::instance ().incomingMessage (call->getCallId (), stripped, module->receive (message, stripped, call->getCallId ())); } @@ -3818,7 +3890,7 @@ mod_on_rx_request (pjsip_rx_data *rdata) call->setConnectionState (Call::Progressing); call->setPeerNumber (peerNumber); call->setDisplayName (displayName); - call->initRecFileName(); + call->initRecFileName (peerNumber); _debug ("UserAgent: DisplayName: %s", displayName.c_str()); diff --git a/sflphone-common/src/sip/sipvoiplink.h b/sflphone-common/src/sip/sipvoiplink.h index e90db00a80e1e08f699300169beac0b40c9b0825..8513725bf6bd01b7002621b4e578bfb232db067e 100644 --- a/sflphone-common/src/sip/sipvoiplink.h +++ b/sflphone-common/src/sip/sipvoiplink.h @@ -37,7 +37,7 @@ #include "voiplink.h" #include "hooks/urlhook.h" -#include "im/InstantMessaging.h" +#include "../im/InstantMessaging.h" ////////////////////////////// /* PJSIP imports */ @@ -364,7 +364,18 @@ class SIPVoIPLink : public VoIPLink */ void shutdownSipTransport (const AccountID& accountID); - bool sendTextMessage (const std::string& callID, const std::string& message); + + /** + * Send a SIP message to a call identified by its callid + * + * @param The InstantMessaging module which contains formating, parsing and sending method + * @param The Id of the call to send the message to + * @param The actual message to be transmitted + * @param The sender of this message (could be another participant of a conference) + * + * @return True if the message is sent without error, false elsewhere + */ + bool sendTextMessage (sfl::InstantMessaging *module, const std::string& callID, const std::string& message, const std::string& from); private: /** diff --git a/sflphone-common/test/configurationtest.cpp b/sflphone-common/test/configurationtest.cpp index e79fc1d07eefe62e2bc6230d9217ed2538f2fc60..e3ff1339dd21484864d768e64ced98d6d483e123 100644 --- a/sflphone-common/test/configurationtest.cpp +++ b/sflphone-common/test/configurationtest.cpp @@ -65,7 +65,6 @@ void ConfigurationTest::testDefaultValueSignalisation() CPPUNIT_ASSERT (Manager::instance().voipPreferences.getPlayDtmf() == true); CPPUNIT_ASSERT (Manager::instance().voipPreferences.getPlayTones() == true); CPPUNIT_ASSERT (Manager::instance().voipPreferences.getPulseLength() == 250); - CPPUNIT_ASSERT (Manager::instance().voipPreferences.getSendDtmfAs() == 0); } void ConfigurationTest::testLoadSIPAccount() diff --git a/sflphone-common/test/instantmessagingtest.cpp b/sflphone-common/test/instantmessagingtest.cpp index 99f49970786253f9b076c9bdc0310415e5864ab5..fb0f49308e57d00641e5906c2632c001d5ef4bac 100644 --- a/sflphone-common/test/instantmessagingtest.cpp +++ b/sflphone-common/test/instantmessagingtest.cpp @@ -34,12 +34,16 @@ #include "instantmessagingtest.h" +#include "expat.h" +#include <stdio.h> + #define MAXIMUM_SIZE 10 #define DELIMITER_CHAR "\n\n" using std::cout; using std::endl; + void InstantMessagingTest::setUp() { _im = new sfl::InstantMessaging (); @@ -139,6 +143,225 @@ void InstantMessagingTest::testSplitMessage () CPPUNIT_ASSERT (messages[size- 1] == very_long_message.substr (maxSize * (size-1))); } +static inline char* duplicateString(char dst[], const char src[], size_t len) +{ + memcpy(dst, src, len); + dst[len] = 0; + return dst; +} + +static void XMLCALL startElementCallback(void *userData, const char *name, const char **atts) +{ + + std::cout << "startElement " << name << std::endl; + + int *nbEntry = (int *)userData; + + char attribute[50]; + char value[50]; + + const char **att; + const char **val; + for (att = atts; *att; att += 2) { + + const char **val = att+1; + + duplicateString(attribute, *att, strlen(*att)); + std::cout << "att: " << attribute << std::endl; + + duplicateString(value, *val, strlen(*val)); + std::cout << "val: " << value << std::endl; + + if (strcmp(attribute, "uri") == 0) { + if((strcmp(value, "sip:alex@example.com") == 0) || + (strcmp(value, "sip:manu@example.com") == 0)) + CPPUNIT_ASSERT(true); + else + CPPUNIT_ASSERT(false); + } + } + + *nbEntry += 1; + +} + +static void XMLCALL endElementCallback(void *userData, const char *name) +{ + // std::cout << "endElement " << name << std::endl; +} + +void InstantMessagingTest::testGenerateXmlUriList () +{ + + std::cout << std::endl; + + // Create a test list with two entries + sfl::InstantMessaging::UriList list; + + sfl::InstantMessaging::UriEntry entry1; + entry1[sfl::IM_XML_URI] = "\"sip:alex@example.com\""; + + sfl::InstantMessaging::UriEntry entry2; + entry2[sfl::IM_XML_URI] = "\"sip:manu@example.com\""; + + list.push_front(entry1); + list.push_front(entry2); + + std::string buffer = _im->generateXmlUriList(list); + CPPUNIT_ASSERT(buffer.size() != 0); + + std::cout << buffer << std::endl; + + // parse the resuling xml (further tests are performed in callbacks) + XML_Parser parser = XML_ParserCreate(NULL); + int nbEntry = 0; + XML_SetUserData(parser, &nbEntry); + XML_SetElementHandler(parser, startElementCallback, endElementCallback); + if (XML_Parse(parser, buffer.c_str(), buffer.size(), 1) == XML_STATUS_ERROR) { + std::cout << "Error: " << XML_ErrorString(XML_GetErrorCode(parser)) + << " at line " << XML_GetCurrentLineNumber(parser) << std::endl; + CPPUNIT_ASSERT(false); + } + XML_ParserFree(parser); + + CPPUNIT_ASSERT(nbEntry == 4); + + CPPUNIT_ASSERT(true); +} + +void InstantMessagingTest::testXmlUriListParsing () +{ + std::string xmlbuffer = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + xmlbuffer.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"); + xmlbuffer.append ("<list>"); + xmlbuffer.append ("<entry uri=\"sip:alex@example.com\" cp:copyControl=\"to\" />"); + xmlbuffer.append ("<entry uri=\"sip:manu@example.com\" cp:copyControl=\"to\" />"); + xmlbuffer.append ("</list>"); + xmlbuffer.append ("</resource-lists>"); + + + sfl::InstantMessaging::UriList list = _im->parseXmlUriList(xmlbuffer); + CPPUNIT_ASSERT(list.size() == 2); + + // An iterator over xml attribute + sfl::InstantMessaging::UriEntry::iterator iterAttr; + + // An iterator over list entries + sfl::InstantMessaging::UriList::iterator iterEntry = list.begin(); + + + while (iterEntry != list.end()) { + sfl::InstantMessaging::UriEntry entry = static_cast<sfl::InstantMessaging::UriEntry> (*iterEntry); + iterAttr = entry.find (sfl::IM_XML_URI); + + if((iterAttr->second == std::string("sip:alex@example.com")) || + (iterAttr->second == std::string("sip:manu@example.com"))) + CPPUNIT_ASSERT(true); + else + CPPUNIT_ASSERT(false); + iterEntry++; + } +} + +void InstantMessagingTest::testGetTextArea () +{ + + std::string formatedText = "--boundary Content-Type: text/plain"; + formatedText.append ("Here is the text area"); + + formatedText.append ("--boundary Content-Type: application/resource-lists+xml"); + formatedText.append ("Content-Disposition: recipient-list"); + formatedText.append ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + formatedText.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"); + formatedText.append ("<list>"); + formatedText.append ("<entry uri=\"sip:alex@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("<entry uri=\"sip:manu@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("</list>"); + formatedText.append ("</resource-lists>"); + formatedText.append ("--boundary--"); + + std::string message = _im->findTextMessage(formatedText); + + std::cout << "message " << message << std::endl; + + CPPUNIT_ASSERT(message == "Here is the text area"); +} + + +void InstantMessagingTest::testGetUriListArea () +{ + std::string formatedText = "--boundary Content-Type: text/plain"; + formatedText.append ("Here is the text area"); + + formatedText.append ("--boundary Content-Type: application/resource-lists+xml"); + formatedText.append ("Content-Disposition: recipient-list"); + formatedText.append ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + formatedText.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"); + formatedText.append ("<list>"); + formatedText.append ("<entry uri=\"sip:alex@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("<entry uri=\"sip:manu@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("</list>"); + formatedText.append ("</resource-lists>"); + formatedText.append ("--boundary--"); + + std::string urilist = _im->findTextUriList(formatedText); + + CPPUNIT_ASSERT(urilist.compare("<?xml version=\"1.0\" encoding=\"UTF-8\"?><resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\"><list><entry uri=\"sip:alex@example.com\" cp:copyControl=\"to\" /><entry uri=\"sip:manu@example.com\" cp:copyControl=\"to\" /></list></resource-lists>") == 0); + + std::cout << "urilist: " << urilist << std::endl; + + sfl::InstantMessaging::UriList list = _im->parseXmlUriList(urilist); + CPPUNIT_ASSERT(list.size() == 2); + + // order may be important, for example to identify message sender + sfl::InstantMessaging::UriEntry entry = list.front(); + CPPUNIT_ASSERT(entry.size() == 2); + + sfl::InstantMessaging::UriEntry::iterator iterAttr = entry.find (sfl::IM_XML_URI); + + if(iterAttr == entry.end()) { + std::cout << "Error, did not found attribute" << std::endl; + CPPUNIT_ASSERT(false); + } + + std::string from = iterAttr->second; + CPPUNIT_ASSERT(from == "sip:alex@example.com"); +} + + +void InstantMessagingTest::testIllFormatedMessage () +{ + bool exceptionCaught = false; + + // SHOULD BE: Content-Type: text/plain + std::string formatedText = "--boundary Content-Ty"; + formatedText.append ("Here is the text area"); + + formatedText.append ("--boundary Content-Type: application/resource-lists+xml"); + formatedText.append ("Content-Disposition: recipient-list"); + formatedText.append ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + formatedText.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"); + formatedText.append ("<list>"); + formatedText.append ("<entry uri=\"sip:alex@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("<entry uri=\"sip:manu@example.com\" cp:copyControl=\"to\" />"); + formatedText.append ("</list>"); + formatedText.append ("</resource-lists>"); + formatedText.append ("--boundary--"); + + try { + std::string message = _im->findTextMessage(formatedText); + } catch (sfl::InstantMessageException &e) { + exceptionCaught = true; + } + + if(exceptionCaught) + CPPUNIT_ASSERT(true); + else + CPPUNIT_ASSERT(false); + +} + + void InstantMessagingTest::tearDown() { delete _im; diff --git a/sflphone-common/test/instantmessagingtest.h b/sflphone-common/test/instantmessagingtest.h index e349096b1189459b9ac28af3c2c86457779e047e..4375471d237cbea97b8f950ad3cb91d3a9f6eb69 100644 --- a/sflphone-common/test/instantmessagingtest.h +++ b/sflphone-common/test/instantmessagingtest.h @@ -37,7 +37,7 @@ #include <assert.h> // Application import -#include "sip/im/InstantMessaging.h" +#include "im/InstantMessaging.h" /* * @file instantmessagingtest.h @@ -56,6 +56,11 @@ class InstantMessagingTest : public CppUnit::TestCase { CPPUNIT_TEST (testSaveSingleMessage); CPPUNIT_TEST (testSaveMultipleMessage); CPPUNIT_TEST (testSplitMessage); + CPPUNIT_TEST (testGenerateXmlUriList); + CPPUNIT_TEST (testXmlUriListParsing); + CPPUNIT_TEST (testGetTextArea); + CPPUNIT_TEST (testGetUriListArea); + CPPUNIT_TEST (testIllFormatedMessage); CPPUNIT_TEST_SUITE_END(); public: @@ -78,7 +83,17 @@ class InstantMessagingTest : public CppUnit::TestCase { void testSaveMultipleMessage (); void testSplitMessage (); - + + void testGenerateXmlUriList (); + + void testXmlUriListParsing (); + + void testGetTextArea (); + + void testGetUriListArea (); + + void testIllFormatedMessage (); + private: sfl::InstantMessaging *_im; }; diff --git a/tools/build-system/launchpad/sflphone-client-gnome/debian/control.karmic b/tools/build-system/launchpad/sflphone-client-gnome/debian/control.karmic index c72878afda415cff757f8bb6da1167c5d431544f..53b4e025bbfa95a8a182b9feb2a54e2ba055cab6 100644 --- a/tools/build-system/launchpad/sflphone-client-gnome/debian/control.karmic +++ b/tools/build-system/launchpad/sflphone-client-gnome/debian/control.karmic @@ -2,13 +2,13 @@ Source: sflphone-client-gnome Maintainer: SavoirFaireLinux Inc <julien.bonjean@savoirfairelinux.com> Section: gnome Priority: optional -Build-Depends: debhelper, libgcc1, autoconf, automake, libtool, libgtk2.0-dev, libdbus-glib-1-dev, libnotify-dev, libebook1.2-dev, check, liblog4c-dev, libgnomeui-dev, gnome-doc-utils, rarian-compat +Build-Depends: debhelper, libgcc1, autoconf, automake, libtool, libgtk2.0-dev, libdbus-glib-1-dev, libnotify-dev, libebook1.2-dev, check, liblog4c-dev, libgnomeui-dev, gnome-doc-utils, rarian-compat, libwebkit-dev Standards-Version: 3.7.3 Package: sflphone-client-gnome Priority: optional Architecture: any -Depends: sflphone-common (=${source:Version}), libdbus-glib-1-2, libgtk2.0-0, libc6, libglib2.0-0, libdbus-glib-1-2, libnotify1, librsvg2-common, liblog4c3, libebook1.2-9, libgnomeui-0 +Depends: sflphone-common (=${source:Version}), libdbus-glib-1-2, libgtk2.0-0, libc6, libglib2.0-0, libdbus-glib-1-2, libnotify1, librsvg2-common, liblog4c3, libebook1.2-9, libgnomeui-0, libwebkit-1.0-2 Replaces: sflphone Conflicts: sflphone Homepage: http://www.sflphone.org diff --git a/tools/build-system/launchpad/sflphone-client-gnome/debian/control.lucid b/tools/build-system/launchpad/sflphone-client-gnome/debian/control.lucid index c72878afda415cff757f8bb6da1167c5d431544f..53b4e025bbfa95a8a182b9feb2a54e2ba055cab6 100644 --- a/tools/build-system/launchpad/sflphone-client-gnome/debian/control.lucid +++ b/tools/build-system/launchpad/sflphone-client-gnome/debian/control.lucid @@ -2,13 +2,13 @@ Source: sflphone-client-gnome Maintainer: SavoirFaireLinux Inc <julien.bonjean@savoirfairelinux.com> Section: gnome Priority: optional -Build-Depends: debhelper, libgcc1, autoconf, automake, libtool, libgtk2.0-dev, libdbus-glib-1-dev, libnotify-dev, libebook1.2-dev, check, liblog4c-dev, libgnomeui-dev, gnome-doc-utils, rarian-compat +Build-Depends: debhelper, libgcc1, autoconf, automake, libtool, libgtk2.0-dev, libdbus-glib-1-dev, libnotify-dev, libebook1.2-dev, check, liblog4c-dev, libgnomeui-dev, gnome-doc-utils, rarian-compat, libwebkit-dev Standards-Version: 3.7.3 Package: sflphone-client-gnome Priority: optional Architecture: any -Depends: sflphone-common (=${source:Version}), libdbus-glib-1-2, libgtk2.0-0, libc6, libglib2.0-0, libdbus-glib-1-2, libnotify1, librsvg2-common, liblog4c3, libebook1.2-9, libgnomeui-0 +Depends: sflphone-common (=${source:Version}), libdbus-glib-1-2, libgtk2.0-0, libc6, libglib2.0-0, libdbus-glib-1-2, libnotify1, librsvg2-common, liblog4c3, libebook1.2-9, libgnomeui-0, libwebkit-1.0-2 Replaces: sflphone Conflicts: sflphone Homepage: http://www.sflphone.org