From d46057ef9a597421285b0c297eb9817e0488fe84 Mon Sep 17 00:00:00 2001 From: Robert Scheck Date: Wed, 29 Sep 2021 19:16:07 +0200 Subject: [PATCH] Added upstream feature patch for GTK+ attended transfers --- baresip-1.1.0-attended-transfer.patch | 532 ++++++++++++++++++++++++++ baresip.spec | 7 +- 2 files changed, 538 insertions(+), 1 deletion(-) create mode 100644 baresip-1.1.0-attended-transfer.patch diff --git a/baresip-1.1.0-attended-transfer.patch b/baresip-1.1.0-attended-transfer.patch new file mode 100644 index 0000000..8c68ff8 --- /dev/null +++ b/baresip-1.1.0-attended-transfer.patch @@ -0,0 +1,532 @@ +From 6b54370a48287662daae5721782978662095a9e4 Mon Sep 17 00:00:00 2001 +From: mbattista +Date: Fri, 7 May 2021 18:17:01 +0200 +Subject: [PATCH] Attended Transfer on GTK (#1435) + +* attended transfer on gtk + +* fix bug in clean number + +* cleanup duplicated code and add ToolTip on how to use it. + +Co-authored-by: Marcel Battista +--- + include/baresip.h | 1 + + modules/gtk/call_window.c | 71 +++++++++++++++++++++++++++++++--- + modules/gtk/dial_dialog.c | 25 +++++++++--- + modules/gtk/gtk_mod.c | 78 +++++++++++++++++++++++++++++++++++++- + modules/gtk/gtk_mod.h | 17 ++++++++- + modules/menu/static_menu.c | 12 ++++-- + src/call.c | 29 ++++++++++++++ + 7 files changed, 216 insertions(+), 17 deletions(-) + +diff --git a/include/baresip.h b/include/baresip.h +index 965bd99d4..a626c9dc3 100644 +--- a/include/baresip.h ++++ b/include/baresip.h +@@ -198,6 +198,7 @@ int call_send_digit(struct call *call, char key); + bool call_has_audio(const struct call *call); + bool call_has_video(const struct call *call); + int call_transfer(struct call *call, const char *uri); ++int call_replace_transfer(struct call *target_call, struct call *source_call); + int call_status(struct re_printf *pf, const struct call *call); + int call_debug(struct re_printf *pf, const struct call *call); + int call_notify_sipfrag(struct call *call, uint16_t scode, +diff --git a/modules/gtk/call_window.c b/modules/gtk/call_window.c +index bb2f5ecfe..451b56256 100644 +--- a/modules/gtk/call_window.c ++++ b/modules/gtk/call_window.c +@@ -23,11 +23,12 @@ struct call_window { + struct vumeter_enc *enc; + } vu; + struct transfer_dialog *transfer_dialog; ++ struct dial_dialog *attended_transfer_dial; + GtkWidget *window; + GtkLabel *status; + GtkLabel *duration; + struct { +- GtkWidget *hangup, *transfer, *hold, *mute; ++ GtkWidget *hangup, *transfer, *hold, *mute, *attended_transfer; + } buttons; + struct { + GtkProgressBar *enc, *dec; +@@ -37,6 +38,7 @@ struct call_window { + bool closed; + int cur_key; + struct play *play_dtmf_tone; ++ struct call *attended_call; + }; + + enum call_window_events { +@@ -45,6 +47,7 @@ enum call_window_events { + MQ_HOLD, + MQ_MUTE, + MQ_TRANSFER, ++ MQ_ATTTRANSFER, + }; + + static pthread_mutex_t last_data_mut = PTHREAD_MUTEX_INITIALIZER; +@@ -196,10 +199,17 @@ static void call_on_hangup(GtkToggleButton *btn, struct call_window *win) + static void call_on_hold_toggle(GtkToggleButton *btn, struct call_window *win) + { + bool hold = gtk_toggle_button_get_active(btn); +- if (hold) ++ if (hold) { ++ gtk_widget_set_sensitive(win->buttons.attended_transfer, ++ TRUE); + vumeter_timer_stop(win); ++ } + else ++ { ++ gtk_widget_set_sensitive(win->buttons.attended_transfer, ++ FALSE); + vumeter_timer_start(win); ++ } + mqueue_push(win->mq, MQ_HOLD, (void *)(size_t)hold); + } + +@@ -221,6 +231,25 @@ static void call_on_transfer(GtkToggleButton *btn, struct call_window *win) + } + + ++static void call_window_transfer_attended_call(GtkToggleButton *btn, ++ struct call_window *win) ++{ ++ (void)btn; ++ mqueue_push(win->mq, MQ_ATTTRANSFER, win); ++} ++ ++ ++static void call_on_attended_transfer(GtkToggleButton *btn, ++ struct call_window *win) ++{ ++ (void)btn; ++ if (!win->attended_transfer_dial) ++ win->attended_transfer_dial = ++ dial_dialog_alloc(win->mod, win->call); ++ dial_dialog_show(win->attended_transfer_dial); ++} ++ ++ + static gboolean call_on_window_close(GtkWidget *widget, GdkEventAny *event, + struct call_window *win) + { +@@ -319,6 +348,10 @@ static void mqueue_handler(int id, void *data, void *arg) + case MQ_TRANSFER: + call_transfer(win->call, data); + break; ++ ++ case MQ_ATTTRANSFER: ++ call_replace_transfer(win->attended_call, win->call); ++ break; + } + } + +@@ -331,12 +364,14 @@ static void call_window_destructor(void *arg) + gtk_mod_call_window_closed(window->mod, window); + gtk_widget_destroy(window->window); + mem_deref(window->transfer_dialog); ++ mem_deref(window->attended_transfer_dial); + gdk_threads_leave(); + + mem_deref(window->call); + mem_deref(window->mq); + mem_deref(window->vu.enc); + mem_deref(window->vu.dec); ++ mem_deref(window->attended_call); + + if (window->duration_timer_tag) + g_source_remove(window->duration_timer_tag); +@@ -349,7 +384,8 @@ static void call_window_destructor(void *arg) + } + + +-struct call_window *call_window_new(struct call *call, struct gtk_mod *mod) ++struct call_window *call_window_new(struct call *call, struct gtk_mod *mod, ++ struct call *attended_call) + { + struct call_window *win; + GtkWidget *window, *label, *status, *button, *progress, *image; +@@ -428,13 +464,33 @@ struct call_window *call_window_new(struct call *call, struct gtk_mod *mod) + GTK_ICON_SIZE_BUTTON); + gtk_button_set_image(GTK_BUTTON(button), image); + +- /* Transfer */ ++ /* Blind Transfer */ + button = gtk_button_new_with_label("Transfer"); + win->buttons.transfer = button; + gtk_box_pack_end(GTK_BOX(button_box), button, FALSE, TRUE, 0); +- g_signal_connect(button, "clicked", G_CALLBACK(call_on_transfer), win); ++ g_signal_connect(button, "clicked", ++ G_CALLBACK(call_on_transfer), win); ++ image = gtk_image_new_from_icon_name("forward", ++ GTK_ICON_SIZE_BUTTON); ++ gtk_button_set_image(GTK_BUTTON(button), image); ++ ++ /* Attended Transfer */ ++ button = gtk_button_new_with_label("Att. Transfer"); ++ win->buttons.attended_transfer = button; ++ gtk_box_pack_end(GTK_BOX(button_box), button, FALSE, TRUE, 0); ++ if (!attended_call) { ++ g_signal_connect(button, "clicked", ++ G_CALLBACK(call_on_attended_transfer), win); ++ } ++ else { ++ g_signal_connect(button, "clicked", ++ G_CALLBACK(call_window_transfer_attended_call), win); ++ } + image = gtk_image_new_from_icon_name("forward", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image(GTK_BUTTON(button), image); ++ gtk_widget_set_sensitive (button, FALSE); ++ gtk_widget_set_tooltip_text(button, ++ "Please put the call on 'Hold' to enable attended transfer"); + + /* Hold */ + button = gtk_toggle_button_new_with_label("Hold"); +@@ -467,9 +523,11 @@ struct call_window *call_window_new(struct call *call, struct gtk_mod *mod) + G_CALLBACK(call_on_key_release), win); + + win->call = mem_ref(call); ++ win->attended_call = mem_ref(attended_call); + win->mod = mod; + win->window = window; + win->transfer_dialog = NULL; ++ win->attended_transfer_dial = NULL; + win->status = GTK_LABEL(status); + win->duration = GTK_LABEL(duration); + win->closed = false; +@@ -509,6 +567,7 @@ void call_window_closed(struct call_window *win, const char *reason) + win->duration_timer_tag = 0; + } + gtk_widget_set_sensitive(win->buttons.transfer, FALSE); ++ gtk_widget_set_sensitive(win->buttons.attended_transfer, FALSE); + gtk_widget_set_sensitive(win->buttons.hold, FALSE); + gtk_widget_set_sensitive(win->buttons.mute, FALSE); + +@@ -522,6 +581,7 @@ void call_window_closed(struct call_window *win, const char *reason) + + call_window_set_status(win, status); + win->transfer_dialog = mem_deref(win->transfer_dialog); ++ win->attended_transfer_dial = mem_deref(win->attended_transfer_dial); + win->closed = true; + + if (reason && strncmp(reason, user_trigger_reason, +@@ -588,3 +648,4 @@ bool call_window_is_for_call(struct call_window *win, struct call *call) + + return win->call == call; + } ++ +diff --git a/modules/gtk/dial_dialog.c b/modules/gtk/dial_dialog.c +index 8590702f5..22abefdd4 100644 +--- a/modules/gtk/dial_dialog.c ++++ b/modules/gtk/dial_dialog.c +@@ -10,12 +10,14 @@ + #include + #include + #include "gtk_mod.h" ++#include + + + struct dial_dialog { + struct gtk_mod *mod; + GtkWidget *dialog; + GtkComboBox *uri_combobox; ++ struct call *attended_call; + }; + + +@@ -26,9 +28,14 @@ static int clean_number(char* str) + /* only clean numeric numbers + * In other cases trust the user input + */ +- int err = re_regex(str, str_len(str), "[A-Za-z]"); +- if (err == 0) +- return -1; ++ while (str[i]) { ++ if (isalpha(str[i] != 0)) ++ return -1; ++ else if (str[i] == '@') ++ return -1; ++ ++i; ++ } ++ i = 0; + + /* remove (0) which is in some mal-formated numbers + * but only if trailed by another character +@@ -77,7 +84,13 @@ static void dial_dialog_on_response(GtkDialog *dialog, gint response_id, + uri_combo_box_set_text(dd->uri_combobox, + uri, length); + } +- gtk_mod_connect(dd->mod, uri); ++ if (!dd->attended_call) { ++ gtk_mod_connect(dd->mod, uri); ++ } ++ else { ++ gtk_mod_connect_attended(dd->mod, uri, ++ dd->attended_call); ++ } + } + + gtk_widget_hide(GTK_WIDGET(dialog)); +@@ -92,7 +105,8 @@ static void destructor(void *arg) + } + + +-struct dial_dialog *dial_dialog_alloc(struct gtk_mod *mod) ++struct dial_dialog *dial_dialog_alloc(struct gtk_mod *mod, ++ struct call *attended_call) + { + struct dial_dialog *dd; + GtkWidget *dial; +@@ -138,6 +152,7 @@ struct dial_dialog *dial_dialog_alloc(struct gtk_mod *mod) + dd->dialog = dial; + dd->uri_combobox = GTK_COMBO_BOX(uri_combobox); + dd->mod = mod; ++ dd->attended_call = attended_call; + + return dd; + } +diff --git a/modules/gtk/gtk_mod.c b/modules/gtk/gtk_mod.c +index 55a806620..5cfa07830 100644 +--- a/modules/gtk/gtk_mod.c ++++ b/modules/gtk/gtk_mod.c +@@ -68,6 +68,7 @@ static struct gtk_mod mod_obj; + enum gtk_mod_events { + MQ_POPUP, + MQ_CONNECT, ++ MQ_CONNECTATTENDED, + MQ_QUIT, + MQ_ANSWER, + MQ_HANGUP, +@@ -143,7 +144,7 @@ static void menu_on_dial(GtkMenuItem *menuItem, gpointer arg) + struct gtk_mod *mod = arg; + (void)menuItem; + if (!mod->dial_dialog) +- mod->dial_dialog = dial_dialog_alloc(mod); ++ mod->dial_dialog = dial_dialog_alloc(mod, NULL); + dial_dialog_show(mod->dial_dialog); + } + +@@ -156,6 +157,7 @@ static void menu_on_dial_contact(GtkMenuItem *menuItem, gpointer arg) + gtk_mod_connect(mod, uri); + } + ++ + static void menu_on_dial_history(GtkMenuItem *menuItem, gpointer arg) + { + struct gtk_mod *mod = arg; +@@ -557,7 +559,19 @@ static void reject_activated(GSimpleAction *action, GVariant *parameter, + static struct call_window *new_call_window(struct gtk_mod *mod, + struct call *call) + { +- struct call_window *win = call_window_new(call, mod); ++ struct call_window *win = call_window_new(call, mod, NULL); ++ if (call) { ++ mod->call_windows = g_slist_append(mod->call_windows, win); ++ } ++ return win; ++} ++ ++ ++static struct call_window *new_call_transfer_window(struct gtk_mod *mod, ++ struct call *call, ++ struct call *attended_call) ++{ ++ struct call_window *win = call_window_new(call, mod, attended_call); + if (call) { + mod->call_windows = g_slist_append(mod->call_windows, win); + } +@@ -785,6 +799,42 @@ int gtk_mod_connect(struct gtk_mod *mod, const char *uri) + } + + ++int gtk_mod_connect_attended(struct gtk_mod *mod, const char *uri, ++ struct call *attended_call) ++{ ++ struct attended_transfer_store *ats; ++ struct mbuf *uribuf = NULL; ++ char *uri_copy = NULL; ++ int err = 0; ++ ++ if (!mod) ++ return ENOMEM; ++ ++ uribuf = mbuf_alloc(64); ++ ats = mem_zalloc(sizeof(struct attended_transfer_store), NULL); ++ if (!uribuf) ++ return ENOMEM; ++ ++ err = account_uri_complete(ua_account(mod->ua_cur), uribuf, uri); ++ if (err) ++ return EINVAL; ++ ++ uribuf->pos = 0; ++ err = mbuf_strdup(uribuf, &uri_copy, uribuf->end); ++ if (err) ++ goto out; ++ ++ ats->uri = (char *)uri_copy; ++ ats->attended_call = attended_call; ++ ++ err = mqueue_push(mod->mq, MQ_CONNECTATTENDED, ats); ++ ++out: ++ mem_deref(uribuf); ++ return err; ++} ++ ++ + bool gtk_mod_clean_number(struct gtk_mod *mod) + { + if (!mod) +@@ -819,6 +869,7 @@ static void mqueue_handler(int id, void *data, void *arg) + struct gtk_mod *mod = arg; + const char *uri; + struct call *call; ++ struct attended_transfer_store *ats; + int err; + struct ua *ua = gtk_current_ua(); + +@@ -851,6 +902,29 @@ static void mqueue_handler(int id, void *data, void *arg) + mem_deref(data); + break; + ++ case MQ_CONNECTATTENDED: ++ ats = data; ++ err = ua_connect(ua, &call, NULL, ats->uri, VIDMODE_ON); ++ add_history_menu_item(mod, ats->uri, CALL_OUTGOING, ""); ++ if (err) { ++ gdk_threads_enter(); ++ warning_dialog("Call failed", ++ "Connecting to \"%s\" failed.\n" ++ "Error: %m", ats->uri, err); ++ gdk_threads_leave(); ++ break; ++ } ++ gdk_threads_enter(); ++ err = new_call_transfer_window(mod, call, ++ ats->attended_call) == NULL; ++ gdk_threads_leave(); ++ if (err) { ++ ua_hangup(ua, call, 500, "Server Error"); ++ } ++ mem_deref(ats->uri); ++ mem_deref(data); ++ break; ++ + case MQ_HANGUP: + call = data; + ua_hangup(ua, call, 0, NULL); +diff --git a/modules/gtk/gtk_mod.h b/modules/gtk/gtk_mod.h +index 2f3bfb4f4..522202d26 100644 +--- a/modules/gtk/gtk_mod.h ++++ b/modules/gtk/gtk_mod.h +@@ -26,15 +26,27 @@ struct vumeter_dec { + volatile bool started; + }; + ++struct attended_transfer_store { ++ struct call *attended_call; ++ char *uri; ++}; ++ ++ + /* Main menu */ + int gtk_mod_connect(struct gtk_mod *, const char *uri); ++int gtk_mod_connect_attended(struct gtk_mod *, const char *uri, ++ struct call *attended_call); ++int gtk_mod_transfer(struct gtk_mod *, const char *uri, ++ struct call *attended_call); + void gtk_mod_call_window_closed(struct gtk_mod *, struct call_window *); + + /* Call Window */ +-struct call_window *call_window_new(struct call *call, struct gtk_mod *mod); ++struct call_window *call_window_new(struct call *call, struct gtk_mod *mod, ++ struct call *attended_call); + void call_window_got_vu_dec(struct vumeter_dec *); + void call_window_got_vu_enc(struct vumeter_enc *); + void call_window_transfer(struct call_window *, const char *uri); ++void call_window_atttransfer(struct call_window *, const char *uri); + void call_window_closed(struct call_window *, const char *reason); + void call_window_ringing(struct call_window *); + void call_window_progress(struct call_window *); +@@ -43,7 +55,8 @@ void call_window_transfer_failed(struct call_window *, const char *reason); + bool call_window_is_for_call(struct call_window *, struct call *); + + /* Dial Dialog */ +-struct dial_dialog *dial_dialog_alloc(struct gtk_mod *); ++struct dial_dialog *dial_dialog_alloc(struct gtk_mod *, ++ struct call *attended_call); + void dial_dialog_show(struct dial_dialog *); + + /* Call transfer dialog */ +diff --git a/modules/menu/static_menu.c b/modules/menu/static_menu.c +index 91bbfd948..1fcc25ca0 100644 +--- a/modules/menu/static_menu.c ++++ b/modules/menu/static_menu.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + #include "menu.h" + +@@ -395,9 +396,14 @@ static void clean_number(char *str) + /* only clean numeric numbers + * In other cases trust the user input + */ +- int err = re_regex(str, str_len(str), "[A-Za-z]"); +- if (err == 0) +- return; ++ while (str[i]) { ++ if (isalpha(str[i] != 0)) ++ return; ++ else if (str[i] == '@') ++ return; ++ ++i; ++ } ++ i = 0; + + /* remove (0) which is in some mal-formated numbers + * but only if trailed by another character +diff --git a/src/call.c b/src/call.c +index d9f35248a..496fcecdb 100644 +--- a/src/call.c ++++ b/src/call.c +@@ -2291,6 +2291,35 @@ int call_transfer(struct call *call, const char *uri) + } + + ++/** ++ * Transfer the call to a target SIP uri and replace the source call ++ * ++ * @param call Call object ++ * @param uri Target SIP uri ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int call_replace_transfer(struct call *call, struct call *source_call) ++{ ++ int err; ++ ++ info("transferring call to %s\n", source_call->peer_uri); ++ ++ call->sub = mem_deref(call->sub); ++ err = sipevent_drefer(&call->sub, uag_sipevent_sock(), ++ sipsess_dialog(call->sess), ua_cuser(call->ua), ++ auth_handler, call->acc, true, ++ sipsub_notify_handler, sipsub_close_handler, ++ call, "Refer-To: %s?Replaces=%s\r\n", ++ source_call->peer_uri, source_call->id); ++ if (err) { ++ warning("call: sipevent_drefer: %m\n", err); ++ } ++ ++ return err; ++} ++ ++ + int call_af(const struct call *call) + { + return call ? call->af : AF_UNSPEC; diff --git a/baresip.spec b/baresip.spec index 84e9a0b..eafb391 100644 --- a/baresip.spec +++ b/baresip.spec @@ -1,7 +1,7 @@ Summary: Modular SIP user-agent with audio and video support Name: baresip Version: 1.1.0 -Release: 4%{?dist} +Release: 5%{?dist} License: BSD URL: https://github.com/baresip/baresip Source0: https://github.com/baresip/baresip/archive/v%{version}/%{name}-%{version}.tar.gz @@ -15,6 +15,7 @@ Patch0: https://github.com/baresip/baresip/commit/68fd6b29d34380f74ea69b Patch1: https://github.com/baresip/baresip/commit/b0669e837daefeaff482a04d6dc15df1c6ebc0f0.patch#/baresip-1.1.0-fritzbox2baresip.patch Patch2: https://github.com/baresip/baresip/commit/01804dcfaf77e298a3f7bdc3d4cfe0882bf681cd.patch#/baresip-1.1.0-module-cairo.patch Patch3: https://github.com/baresip/baresip/commit/0a1dc2492856a22a4551b02dc5a866322a7d514d.patch#/baresip-1.1.0-module-oss.patch +Patch4: https://github.com/baresip/baresip/commit/6b54370a48287662daae5721782978662095a9e4.patch#/baresip-1.1.0-attended-transfer.patch BuildRequires: make BuildRequires: gcc BuildRequires: libre-devel >= 2.0.0 @@ -394,6 +395,7 @@ This module provides the X11 grabber video source driver. %patch1 -p1 -b .fritzbox2baresip %patch2 -p1 -b .module-cairo %patch3 -p1 -b .module-oss +%patch4 -p1 -b .attended-transfer %build %if 0%{?rhel} == 7 @@ -607,6 +609,9 @@ gtk-update-icon-cache --force %{_datadir}/icons/Adwaita &>/dev/null || : %{_libdir}/%{name}/modules/x11grab.so %changelog +* Wed Sep 29 2021 Robert Scheck 1.1.0-5 +- Added upstream feature patch for GTK+ attended transfers + * Wed Aug 11 2021 Robert Scheck 1.1.0-4 - Rebuilt for codec2 1.0.0 (#1991468)