From d798d9ab4bb9f0cdfe483c5a3eac22c0f80cd990 Mon Sep 17 00:00:00 2001 From: Kaian Date: Tue, 18 Mar 2014 12:05:11 +0100 Subject: [PATCH] Implemented a filter UI for Call list --- config/Makefile.am | 2 +- configure | 56 +++++++- configure.ac | 10 +- src/Makefile.am | 2 +- src/Makefile.in | 6 +- src/main.c | 2 +- src/option.c | 18 ++- src/sip.c | 39 ++++++ src/ui_call_flow.c | 2 +- src/ui_call_flow.h | 2 +- src/ui_call_list.c | 74 ++++++++--- src/ui_call_list.h | 19 ++- src/ui_call_raw.c | 2 +- src/ui_call_raw.h | 2 +- src/ui_filter.c | 318 +++++++++++++++++++++++++++++++++++++++++++++ src/ui_filter.h | 128 ++++++++++++++++++ src/ui_manager.c | 29 +++-- src/ui_manager.h | 2 + 18 files changed, 666 insertions(+), 47 deletions(-) create mode 100644 src/ui_filter.c create mode 100644 src/ui_filter.h diff --git a/config/Makefile.am b/config/Makefile.am index 05ff2fe..0ecc9ee 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -1,2 +1,2 @@ -sysconfdir = @sysconfdir@ +sysconfdir=@sysconfdir@ sysconf_DATA=sngreprc diff --git a/configure b/configure index 09021e3..8728d2d 100755 --- a/configure +++ b/configure @@ -4133,8 +4133,8 @@ fi if test "$enable_debug" = "yes" ; then - CFLAGS="$CFLAGS -g -O0 -Wall -Werror -Wno-uninitialized" - CXXFLAGS="$CXXFLAGS -g -O0 -Wall -Werror -Wno-uninitialized" + CFLAGS="$CFLAGS -g -O0 -Wall -Werror -Wno-unused-but-set-variable" + CXXFLAGS="$CXXFLAGS $CFLAGS" fi # Minimum checks for a C program :) @@ -5411,7 +5411,57 @@ _ACEOF else - as_fn_error $? " You need to have libpanel installed to compile sngrep." "$LINENO" 5 + as_fn_error $? " You need to have ncurses panel library installed to compile sngrep." "$LINENO" 5 + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for new_form in -lform" >&5 +$as_echo_n "checking for new_form in -lform... " >&6; } +if ${ac_cv_lib_form_new_form+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lform $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char new_form (); +int +main () +{ +return new_form (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_form_new_form=yes +else + ac_cv_lib_form_new_form=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_form_new_form" >&5 +$as_echo "$ac_cv_lib_form_new_form" >&6; } +if test "x$ac_cv_lib_form_new_form" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBFORM 1 +_ACEOF + + LIBS="-lform $LIBS" + +else + + as_fn_error $? " You need to have ncurses forms library installed to compile sngrep." "$LINENO" 5 fi diff --git a/configure.ac b/configure.ac index 1a7dde9..086b6b5 100644 --- a/configure.ac +++ b/configure.ac @@ -14,8 +14,8 @@ AC_ARG_ENABLE(debug, enable_debug=$enableval, enable_debug=no) if test "$enable_debug" = "yes" ; then - CFLAGS="$CFLAGS -g -O0 -Wall -Werror -Wno-uninitialized" - CXXFLAGS="$CXXFLAGS -g -O0 -Wall -Werror -Wno-uninitialized" + CFLAGS="$CFLAGS -g -O0 -Wall -Werror -Wno-unused-but-set-variable" + CXXFLAGS="$CXXFLAGS $CFLAGS" fi # Minimum checks for a C program :) @@ -54,7 +54,11 @@ AC_CHECK_LIB([ncurses], [initscr], [], [ ]) AC_CHECK_LIB([panel], [new_panel], [], [ - AC_MSG_ERROR([ You need to have libpanel installed to compile sngrep.]) + AC_MSG_ERROR([ You need to have ncurses panel library installed to compile sngrep.]) +]) + +AC_CHECK_LIB([form], [new_form], [], [ + AC_MSG_ERROR([ You need to have ncurses forms library installed to compile sngrep.]) ]) AC_CHECK_LIB([pthread], [pthread_create], [], [ diff --git a/src/Makefile.am b/src/Makefile.am index 4301e39..c0ccfef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,2 +1,2 @@ bin_PROGRAMS=sngrep -sngrep_SOURCES=exec.c spcap.c sip.c main.c option.c group.c ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c +sngrep_SOURCES=exec.c spcap.c sip.c main.c option.c group.c ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c ui_filter.c diff --git a/src/Makefile.in b/src/Makefile.in index 63de7b1..7956eca 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -64,7 +64,8 @@ PROGRAMS = $(bin_PROGRAMS) am_sngrep_OBJECTS = exec.$(OBJEXT) spcap.$(OBJEXT) sip.$(OBJEXT) \ main.$(OBJEXT) option.$(OBJEXT) group.$(OBJEXT) \ ui_manager.$(OBJEXT) ui_call_list.$(OBJEXT) \ - ui_call_flow.$(OBJEXT) ui_call_raw.$(OBJEXT) + ui_call_flow.$(OBJEXT) ui_call_raw.$(OBJEXT) \ + ui_filter.$(OBJEXT) sngrep_OBJECTS = $(am_sngrep_OBJECTS) sngrep_LDADD = $(LDADD) DEFAULT_INCLUDES = -I.@am__isrc@ @@ -191,7 +192,7 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -sngrep_SOURCES = exec.c spcap.c sip.c main.c option.c group.c ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c +sngrep_SOURCES = exec.c spcap.c sip.c main.c option.c group.c ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c ui_filter.c all: all-am .SUFFIXES: @@ -285,6 +286,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_call_flow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_call_list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_call_raw.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_filter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui_manager.Po@am__quote@ .c.o: diff --git a/src/main.c b/src/main.c index ec6fa72..0c70838 100644 --- a/src/main.c +++ b/src/main.c @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) { - int ret; + int ret = 0; //! ngrep thread attributes pthread_attr_t attr; //! ngrep running thread diff --git a/src/option.c b/src/option.c index 5a1cea2..7aa52ab 100644 --- a/src/option.c +++ b/src/option.c @@ -75,6 +75,20 @@ init_options() // Allow dialogs to be incomplete set_option_value("sip.ignoreincomlete", "0"); + // Toggle capture + set_option_value("sip.capture", "on"); + + // Set default filter options + set_option_value("filter.enable", "off"); + set_option_value("filter.REGISTER", "on"); + set_option_value("filter.INVITE", "on"); + set_option_value("filter.INVITE (SDP)", "on"); + set_option_value("filter.SUBSCRIBE", "on"); + set_option_value("filter.NOTIFY", "on"); + set_option_value("filter.OPTIONS", "on"); + set_option_value("filter.PUBLISH", "on"); + set_option_value("filter.MESSAGE", "on"); + // Read options from configuration files read_options("/etc/sngreprc"); read_options("/usr/local/etc/sngreprc"); @@ -150,12 +164,12 @@ set_option_value(const char *opt, const char *value) if (!get_option_value(opt)) { options[optscnt].type = SETTING; options[optscnt].opt = opt; - options[optscnt].value = value; + options[optscnt].value = strdup(value); optscnt++; } else { for (i = 0; i < optscnt; i++) { if (!strcasecmp(opt, options[i].opt)) { - options[i].value = value; + options[i].value = strdup(value); } } } diff --git a/src/sip.c b/src/sip.c index 8fe75de..38746b4 100644 --- a/src/sip.c +++ b/src/sip.c @@ -176,6 +176,11 @@ sip_load_message(const char *header, const char *payload) sip_call_t *call; char *callid; + // Skip messages if capture is disabled + if (!is_option_enabled("sip.capture")) { + return NULL; + } + // Get the Call-ID of this message if (!(callid = sip_get_callid(payload))) { return NULL; @@ -235,11 +240,45 @@ int sip_check_call_ignore(sip_call_t *call) { int i; + char filter_option[80]; + const char *filter; + // Check if an ignore option exists for (i = 0; i < sizeof(attrs) / sizeof(*attrs); i++) { if (is_ignored_value(attrs[i].name, call_get_attribute(call, attrs[i].id))) { return 1; } } + + // Check enabled filters + if (is_option_enabled("filter.enable")) { + if ((filter = get_option_value("filter.sipfrom")) && strlen(filter)) { + if(strstr(call_get_attribute(call, SIP_ATTR_SIPFROM), filter) == NULL) { + return 1; + } + } + if ((filter = get_option_value("filter.sipto")) && strlen(filter)) { + if(strstr(call_get_attribute(call, SIP_ATTR_SIPTO), filter) == NULL) { + return 1; + } + } + if ((filter = get_option_value("filter.src")) && strlen(filter)) { + if(strncasecmp(filter, call_get_attribute(call, SIP_ATTR_SRC), strlen(filter))) { + return 1; + } + } + if ((filter = get_option_value("filter.dst")) && strlen(filter)) { + if (strncasecmp(filter, call_get_attribute(call, SIP_ATTR_DST), strlen(filter))) { + return 1; + } + } + + // Check if a filter option exists + memset(filter_option, 0, sizeof(filter_option)); + sprintf(filter_option, "filter.%s", call_get_attribute(call, SIP_ATTR_STARTING)); + if (!is_option_enabled(filter_option)) { + return 1; + } + } return 0; } diff --git a/src/ui_call_flow.c b/src/ui_call_flow.c index f7e4b3d..03f5ed4 100644 --- a/src/ui_call_flow.c +++ b/src/ui_call_flow.c @@ -435,7 +435,7 @@ call_flow_handle_key(PANEL *panel, int key) wait_for_input(next_panel); break; default: - return -1; + return key; } return 0; diff --git a/src/ui_call_flow.h b/src/ui_call_flow.h index 9998802..3276961 100644 --- a/src/ui_call_flow.h +++ b/src/ui_call_flow.h @@ -132,7 +132,7 @@ call_flow_draw_raw(PANEL *panel, sip_msg_t *msg); * * @param panel Ncurses panel pointer * @param key Pressed keycode - * @return 0 if the function can handle the key, -1 otherwise + * @return 0 if the function can handle the key, key otherwise */ extern int call_flow_handle_key(PANEL *panel, int key); diff --git a/src/ui_call_list.c b/src/ui_call_list.c index 94e757a..c3630ac 100644 --- a/src/ui_call_list.c +++ b/src/ui_call_list.c @@ -94,10 +94,9 @@ call_list_create() mvwaddch(win, 7, width - 1, ACS_RTEE); mvwprintw(win, height - 2, 2, "Q/Esc: Quit"); mvwprintw(win, height - 2, 16, "F1: Help"); - mvwprintw(win, height - 2, 27, "x: Call-Flow Extended"); - mvwprintw(win, height - 2, 52, "r: Call Raw"); - mvwprintw(win, height - 2, 67, "c: Colours"); - mvwprintw(win, height - 2, 80, "Space: Select dialog"); + mvwprintw(win, height - 2, 27, "Enter/x: Call-Flow"); + mvwprintw(win, height - 2, 48, "p: pause capture"); + mvwprintw(win, height - 2, 67, "Space: Select dialog"); // Draw columns titles for (colpos = 6, i = 0; i < info->columncnt; i++) { @@ -147,6 +146,13 @@ call_list_draw(PANEL *panel) call_list_info_t *info = (call_list_info_t*) panel_userptr(panel); if (!info) return -1; + // Get window of call list panel + WINDOW *win = panel_window(panel); + getmaxyx(win, height, width); + + // Print in the header if we're actually capturing + mvwprintw(win, 3, 23, "%s", is_option_enabled("sip.capture")?" ":"(Paused)"); + // Get available calls counter (we'll use it here a couple of times) if (!(callcnt = sip_calls_count())) return 0; @@ -156,10 +162,6 @@ call_list_draw(PANEL *panel) info->cur_line = info->first_line = 1; } - // Get window of call list panel - WINDOW *win = panel_window(panel); - getmaxyx(win, height, width); - // Fill the call list int cline = startline; for (call = info->first_call; call; call = call_get_next(call)) { @@ -303,7 +305,7 @@ call_list_handle_key(PANEL *panel, int key) break; case 'r': case 'R': - // KEY_X , Display current call flow (extended) + // KEY_R , Display current call flow (extended) next_panel = ui_create(ui_find_by_type(RAW_PANEL)); if (info->group->callcnt) { group = info->group; @@ -315,6 +317,13 @@ call_list_handle_key(PANEL *panel, int key) call_raw_set_group(group); wait_for_input(next_panel); break; + case 'f': + case 'F': + // KEY_F, Display filter panel + next_panel = ui_create(ui_find_by_type(FILTER_PANEL)); + wait_for_input(next_panel); + call_list_filter_update(panel); + break; case ' ': if (!info->cur_call) return -1; if (call_group_exists(info->group, info->cur_call)) { @@ -331,7 +340,7 @@ call_list_handle_key(PANEL *panel, int key) } break; default: - return -1; + return key; } return 0; @@ -344,7 +353,7 @@ call_list_help(PANEL *panel) PANEL *help_panel; // Create a new panel and show centered - help_win = newwin(20, 65, (LINES - 20) / 2, (COLS - 65) / 2); + help_win = newwin(21, 65, (LINES - 21) / 2, (COLS - 65) / 2); help_panel = new_panel(help_win); // Set the window title @@ -355,16 +364,16 @@ call_list_help(PANEL *panel) box(help_win, 0, 0); mvwhline(help_win, 2, 1, ACS_HLINE, 63); mvwhline(help_win, 7, 1, ACS_HLINE, 63); - mvwhline(help_win, 17, 1, ACS_HLINE, 63); + mvwhline(help_win, 18, 1, ACS_HLINE, 63); mvwaddch(help_win, 2, 0, ACS_LTEE); mvwaddch(help_win, 7, 0, ACS_LTEE); - mvwaddch(help_win, 17, 0, ACS_LTEE); + mvwaddch(help_win, 18, 0, ACS_LTEE); mvwaddch(help_win, 2, 64, ACS_RTEE); mvwaddch(help_win, 7, 64, ACS_RTEE); - mvwaddch(help_win, 17, 64, ACS_RTEE); + mvwaddch(help_win, 18, 64, ACS_RTEE); // Set the window footer (nice blue?) - mvwprintw(help_win, 18, 20, "Press any key to continue"); + mvwprintw(help_win, 19, 20, "Press any key to continue"); // Some brief explanation abotu what window shows wattron(help_win, COLOR_PAIR(HELP_COLOR)); @@ -383,6 +392,7 @@ call_list_help(PANEL *panel) mvwprintw(help_win, 14, 2, "Enter Show selected call-flow."); mvwprintw(help_win, 15, 2, "x Show selected call-flow (Extended) if available."); mvwprintw(help_win, 16, 2, "r Show selected call messages in raw mode."); + mvwprintw(help_win, 17, 2, "p Pause. Stop capturing packages"); // Press any key to close wgetch(help_win); @@ -448,9 +458,9 @@ call_list_exit_confirm(PANEL *panel) exit = (exit)?0:1; break; case 10: - // If we return 1, we let ui_manager to handle this + // If we return ESC, we let ui_manager to handle this // key and exit sngrep gracefully - return exit; + return (exit)?27:0; } } @@ -471,3 +481,33 @@ call_list_add_column(PANEL *panel, enum sip_attr_id id, const char* attr, const info->columncnt++; return 0; } + +void +call_list_filter_update(PANEL *panel) +{ + + WINDOW *win = panel_window(panel); + int height, width, i, startline = 8; + + // Get panel info + call_list_info_t *info = (call_list_info_t*) panel_userptr(panel); + if (!info) return; + + // Initialize structures + info->first_call = info->cur_call = NULL; + info->first_line = info->cur_line = 0; + info->group->callcnt = 0; + + // Get Window dimensions + getmaxyx(win, height, width); + + // Clear Displayed lines + for (i=0; i < info->linescnt; i++) { + mvwprintw(win, startline++, 5, "%*s", width - 6, ""); + } + + // Print filter info + mvwprintw(win, 4, 2, "%*s", width - 4, ""); + if (is_option_enabled("filter.enable")) + mvwprintw(win, 4, 2, "%s", "Display filter: TODO"); +} diff --git a/src/ui_call_list.h b/src/ui_call_list.h index e17d6ca..4bebc1d 100644 --- a/src/ui_call_list.h +++ b/src/ui_call_list.h @@ -136,7 +136,7 @@ call_list_draw(PANEL *panel); * * @param panel Ncurses panel pointer * @param key Pressed keycode - * @return 0 if the function can handle the key, -1 otherwise + * @return 0 if the function can handle the key, key otherwise */ extern int call_list_handle_key(PANEL *panel, int key); @@ -162,13 +162,12 @@ call_list_help(PANEL *panel); * The default button can be configured using option * cl.defexitbutton (default 1, that means yes) * - * @param panel Ncurses panel pointer - * @return 0 if user selects yes, 1 otherwise + * @param panel Call list panel pointer + * @return 27 if user confirmed exit, 0 otherwise */ extern int call_list_exit_confirm(PANEL *panel); - /** * @brief Add a column the Call List * @@ -185,4 +184,16 @@ call_list_exit_confirm(PANEL *panel); extern int call_list_add_column(PANEL *panel, enum sip_attr_id id, const char* attr, const char *title, int width); +/** + * @brief Update list information after a filter has been set + * + * This function is called after showing the filter dialog (@see ui_filter.h) + * and resets the displayed information to force a new call list + * load. + * + * @param panel Call list panel pointer + */ +extern void +call_list_filter_update(PANEL *panel); + #endif diff --git a/src/ui_call_raw.c b/src/ui_call_raw.c index 7c2922c..77f7d2a 100644 --- a/src/ui_call_raw.c +++ b/src/ui_call_raw.c @@ -160,7 +160,7 @@ call_raw_handle_key(PANEL *panel, int key) call_raw_handle_key(panel, KEY_UP); break; default: - return -1; + return key; } return 0; } diff --git a/src/ui_call_raw.h b/src/ui_call_raw.h index 1eb1f7d..6624397 100644 --- a/src/ui_call_raw.h +++ b/src/ui_call_raw.h @@ -102,7 +102,7 @@ call_raw_print_msg(PANEL *panel, sip_msg_t *msg); * * @param panel Ncurses panel pointer * @param key Pressed keycode - * @return 0 if the function can handle the key, -1 otherwise + * @return 0 if the function can handle the key, key otherwise */ extern int call_raw_handle_key(PANEL *panel, int key); diff --git a/src/ui_filter.c b/src/ui_filter.c new file mode 100644 index 0000000..a984f0d --- /dev/null +++ b/src/ui_filter.c @@ -0,0 +1,318 @@ +/************************************************************************** + ** + ** sngrep - SIP callflow viewer using ngrep + ** + ** Copyright (C) 2014 Ivan Alonso (Kaian) + ** Copyright (C) 2014 Irontec SL. All rights reserved. + ** + ** 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, see . + ** + ****************************************************************************/ +/** + * @file ui_call_raw.c + * @author Ivan Alonso [aka Kaian] + * + * @brief Source of functions defined in ui_call_raw.h + * + * @todo Code help screen. Please. + * @todo Replace the panel refresh. Wclear sucks on high latency conections. + * + */ +#include +#include +#include +#include "ui_filter.h" +#include "option.h" + +PANEL * +filter_create() +{ + PANEL *panel; + WINDOW *win; + int height, width; + filter_info_t *info; + + // Calculate window dimensions + height = 17; + width = 50; + + // Cerate a new indow for the panel and form + win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); + + // Create a new panel + panel = new_panel(win); + + // Initialize Filter panel specific data + info = malloc(sizeof(filter_info_t)); + memset(info, 0, sizeof(filter_info_t)); + + // Store it into panel userptr + set_panel_userptr(panel, (void*) info); + + // Initialize the fields + info->fields[FIELD_ENABLE] = new_field(1, 1, 3, 19, 0, 0); + info->fields[FIELD_SIPFROM] = new_field(1, 28, 5, 18, 0, 0); + info->fields[FIELD_SIPTO] = new_field(1, 28, 6, 18, 0, 0); + info->fields[FIELD_SRC] = new_field(1, 18, 7, 18, 0, 0); + info->fields[FIELD_DST] = new_field(1, 18, 8, 18, 0, 0); + info->fields[FIELD_REGISTER] = new_field(1, 1, 10, 15, 0, 0); + info->fields[FIELD_INVITE] = new_field(1, 1, 11, 15, 0, 0); + info->fields[FIELD_SUBSCRIBE] = new_field(1, 1, 12, 15, 0, 0); + info->fields[FIELD_NOTIFY] = new_field(1, 1, 13, 15, 0, 0); + info->fields[FIELD_OPTIONS] = new_field(1, 1, 10, 37, 0, 0); + info->fields[FIELD_PUBLISH] = new_field(1, 1, 11, 37, 0, 0); + info->fields[FIELD_MESSAGE] = new_field(1, 1, 12, 37, 0, 0); + info->fields[FIELD_FILTER] = new_field(1, 10, height - 2, 11, 0, 0); + info->fields[FIELD_CANCEL] = new_field(1, 10, height - 2, 30, 0, 0); + info->fields[FIELD_COUNT] = NULL; + + // Set fields options + field_opts_off(info->fields[FIELD_ENABLE], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_SIPFROM], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_SIPTO], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_SRC], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_DST], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_REGISTER], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_INVITE], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_SUBSCRIBE], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_NOTIFY], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_OPTIONS], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_PUBLISH], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_MESSAGE], O_AUTOSKIP); + field_opts_off(info->fields[FIELD_FILTER], O_EDIT); + field_opts_off(info->fields[FIELD_CANCEL], O_EDIT); + + // Change background of input fields + set_field_back(info->fields[FIELD_SIPFROM], A_UNDERLINE); + set_field_back(info->fields[FIELD_SIPTO], A_UNDERLINE); + set_field_back(info->fields[FIELD_SRC], A_UNDERLINE); + set_field_back(info->fields[FIELD_DST], A_UNDERLINE); + + // Create the form and post it + info->form = new_form(info->fields); + set_form_sub(info->form, win); + post_form(info->form); + + // Fields labels + mvwprintw(win, 3, 3, "Enable filters [ ]"); + mvwprintw(win, 5, 3, "SIP From:"); + mvwprintw(win, 6, 3, "SIP To:"); + mvwprintw(win, 7, 3, "Source:"); + mvwprintw(win, 8, 3, "Destiny:"); + mvwprintw(win, 10, 3, "REGISTER [ ]"); + mvwprintw(win, 11, 3, "INVITE [ ]"); + mvwprintw(win, 12, 3, "SUBSCRIBE [ ]"); + mvwprintw(win, 13, 3, "NOTIFY [ ]"); + mvwprintw(win, 10, 25, "OPTIONS [ ]"); + mvwprintw(win, 11, 25, "PUBLISH [ ]"); + mvwprintw(win, 12, 25, "MESSAGE [ ]"); + + // Set Default field values + set_field_buffer(info->fields[FIELD_ENABLE], 0, "*"); + set_field_buffer(info->fields[FIELD_SIPFROM], 0, get_option_value("filter.sipfrom")); + set_field_buffer(info->fields[FIELD_SIPTO], 0, get_option_value("filter.sipto")); + set_field_buffer(info->fields[FIELD_SRC], 0, get_option_value("filter.src")); + set_field_buffer(info->fields[FIELD_DST], 0, get_option_value("filter.dst")); + set_field_buffer(info->fields[FIELD_REGISTER], 0, is_option_enabled("filter.REGISTER")?"*":""); + set_field_buffer(info->fields[FIELD_INVITE], 0, is_option_enabled("filter.INVITE")?"*":""); + set_field_buffer(info->fields[FIELD_SUBSCRIBE], 0, is_option_enabled("filter.SUBSCRIBE")?"*":""); + set_field_buffer(info->fields[FIELD_NOTIFY], 0, is_option_enabled("filter.NOTIFY")?"*":""); + set_field_buffer(info->fields[FIELD_OPTIONS], 0, is_option_enabled("filter.OPTIONS")?"*":""); + set_field_buffer(info->fields[FIELD_PUBLISH], 0, is_option_enabled("filter.PUBLISH")?"*":""); + set_field_buffer(info->fields[FIELD_MESSAGE], 0, is_option_enabled("filter.MESSAGE")?"*":""); + set_field_buffer(info->fields[FIELD_FILTER], 0, "[ Filter ]"); + set_field_buffer(info->fields[FIELD_CANCEL], 0, "[ Cancel ]"); + + // Set the window title and boxes + mvwprintw(win, 1, 18, "Filter options"); + wattron(win, COLOR_PAIR(DETAIL_BORDER_COLOR)); + title_foot_box(panel_window(panel)); + mvwhline(win, 4, 1, ACS_HLINE, 49); + mvwaddch(win, 4, 0, ACS_LTEE); + mvwaddch(win, 4, 49, ACS_RTEE); + mvwhline(win, 9, 1, ACS_HLINE, 49); + mvwaddch(win, 9, 0, ACS_LTEE); + mvwaddch(win, 9, 49, ACS_RTEE); + wattroff(win, COLOR_PAIR(DETAIL_BORDER_COLOR)); + + // Set default cursor position + set_current_field(info->form, info->fields[FIELD_SIPFROM]); + wmove(win, 5, 18); + curs_set(1); + + return panel; +} + +void +filter_destroy(PANEL *panel) +{ + // Disable cursor position + curs_set(0); +} + +int +filter_handle_key(PANEL *panel, int key) +{ + int field_idx; + char field_value[30]; + + // Get panel information + filter_info_t *info = (filter_info_t*) panel_userptr(panel); + + // Get current field id + field_idx = field_index(current_field(info->form)); + + // Get current field value. + // We trim spaces with sscanf because and empty field is stored as + // space characters + memset(field_value, 0, sizeof(field_value)); + sscanf(field_buffer(current_field(info->form), 0), "%[^ ]", field_value); + + switch(key) { + case 9 /*KEY_TAB*/: + case KEY_DOWN: + form_driver(info->form, REQ_NEXT_FIELD); + form_driver(info->form, REQ_END_LINE); + break; + case KEY_UP: + form_driver(info->form, REQ_PREV_FIELD); + form_driver(info->form, REQ_END_LINE); + break; + case KEY_RIGHT: + form_driver(info->form, REQ_RIGHT_CHAR); + break; + case KEY_LEFT: + form_driver(info->form, REQ_LEFT_CHAR); + break; + case KEY_HOME: + form_driver(info->form, REQ_BEG_LINE); + break; + case KEY_END: + form_driver(info->form, REQ_END_LINE); + break; + case KEY_DC: + form_driver(info->form, REQ_DEL_CHAR); + break; + case 27 /*KEY_ESC*/: + return key; + break; + case KEY_BACKSPACE: + if (strlen(field_value) > 0) + form_driver(info->form, REQ_DEL_PREV); + break; + case ' ': + switch(field_idx) { + case FIELD_ENABLE: + case FIELD_REGISTER: + case FIELD_INVITE: + case FIELD_SUBSCRIBE: + case FIELD_NOTIFY: + case FIELD_OPTIONS: + case FIELD_PUBLISH: + case FIELD_MESSAGE: + if (field_value[0] == '*') { + form_driver(info->form, REQ_DEL_CHAR); + } else { + form_driver(info->form, '*'); + } + break; + default: + form_driver(info->form, REQ_NEXT_FIELD); + form_driver(info->form, REQ_END_LINE); + break; + } + break; + case 10: /* KEY_ENTER */ + if (field_idx != FIELD_CANCEL) + filter_save_options(panel); + return 27; + default: + // If this is a normal character on input field, print it + switch(field_idx) { + case FIELD_SIPFROM: + case FIELD_SIPTO: + case FIELD_SRC: + case FIELD_DST: + form_driver(info->form, key); + break; + } + break; + } + + // Validate all input data + form_driver(info->form, REQ_VALIDATION); + + // Change background and cursor of "button fields" + set_field_back(info->fields[FIELD_FILTER], A_NORMAL); + set_field_back(info->fields[FIELD_CANCEL], A_NORMAL); + curs_set(1); + + // Change current field background + field_idx = field_index(current_field(info->form)); + if (field_idx == FIELD_FILTER || field_idx == FIELD_CANCEL) { + set_field_back(info->fields[field_idx], A_REVERSE); + curs_set(0); + } + + return 0; +} + +void +filter_save_options(PANEL *panel) +{ + char field_value[30]; + int i; + + // Get panel information + filter_info_t *info = (filter_info_t*) panel_userptr(panel); + + for (i = 0; i < FIELD_COUNT; i++) { + // Get current field value. + // We trim spaces with sscanf because and empty field is stored as + // space characters + memset(field_value, 0, sizeof(field_value)); + sscanf(field_buffer(info->fields[i], 0), "%[^ ]", field_value); + + switch(i) { + case FIELD_ENABLE: + set_option_value("filter.enable", strlen(field_value)?"on":"off"); break; + case FIELD_SIPFROM: + set_option_value("filter.sipfrom", field_value); break; + case FIELD_SIPTO: + set_option_value("filter.sipto", field_value); break; + case FIELD_SRC: + set_option_value("filter.src", field_value); break; + case FIELD_DST: + set_option_value("filter.dst", field_value); break; + case FIELD_REGISTER: + set_option_value("filter.REGISTER", strlen(field_value)?"on":"off"); break; + case FIELD_INVITE: + set_option_value("filter.INVITE", strlen(field_value)?"on":"off"); + set_option_value("filter.INVITE (SDP)", strlen(field_value)?"on":"off"); break; + case FIELD_SUBSCRIBE: + set_option_value("filter.SUBSCRIBE", strlen(field_value)?"on":"off"); break; + case FIELD_NOTIFY: + set_option_value("filter.NOTIFY", strlen(field_value)?"on":"off"); break; + case FIELD_OPTIONS: + set_option_value("filter.OPTIONS", strlen(field_value)?"on":"off"); break; + case FIELD_PUBLISH: + set_option_value("filter.PUBLISH", strlen(field_value)?"on":"off"); break; + case FIELD_MESSAGE: + set_option_value("filter.MESSAGE", strlen(field_value)?"on":"off"); break; + default: + break; + } + } +} + diff --git a/src/ui_filter.h b/src/ui_filter.h new file mode 100644 index 0000000..154d874 --- /dev/null +++ b/src/ui_filter.h @@ -0,0 +1,128 @@ +/************************************************************************** + ** + ** sngrep - SIP callflow viewer using ngrep + ** + ** Copyright (C) 2014 Ivan Alonso (Kaian) + ** Copyright (C) 2014 Irontec SL. All rights reserved. + ** + ** 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, see . + ** + ****************************************************************************/ +/** + * @file ui_filter.h + * @author Ivan Alonso [aka Kaian] + * + * @brief Functions to manage ui window for filtering options + * + * This file contains the functions and structures to manage the filter + * dialog, that can be used to filter the lines in call list window. + */ + +#ifndef __UI_FILTER_H +#define __UI_FILTER_H +#include +#include "ui_manager.h" + +/** + * @brief Enum of available dialog fields + * + * Dialog form has a field array. Following enum represents the + * order this fields are stored in panel info structure. + * + */ +enum field_list { + FIELD_ENABLE = 0, + FIELD_SIPFROM, + FIELD_SIPTO, + FIELD_SRC, + FIELD_DST, + FIELD_REGISTER, + FIELD_INVITE, + FIELD_SUBSCRIBE, + FIELD_NOTIFY, + FIELD_OPTIONS, + FIELD_PUBLISH, + FIELD_MESSAGE, + FIELD_FILTER, + FIELD_CANCEL, + //! Never remove this field id @see filte_info + FIELD_COUNT, +}; + +//! Sorter declaration of struct filter_info +typedef struct filter_info filter_info_t; + +/** + * @brief Filter panel private information + * + * This structure contains the durable data of filter panel. + */ +struct filter_info +{ + //! Form that contains the filter fields + FORM *form; + //! An array of fields + FIELD *fields[FIELD_COUNT]; +}; + +/** + * @brief Creates a new filter panel + * + * This function allocates all required memory for + * displaying the filter panel. It also draws all the + * static information of the panel that will never be + * redrawn. + * + * @return a panel pointer + */ +extern PANEL * +filter_create(); + +/** + * @brief Destroy filter panel + * + * This function do the final cleanups for this panel + */ +extern void +filter_destroy(); + +/** + * @brief Manage pressed keys for filter panel + * + * This function is called by UI manager every time a + * key is pressed. This allow the filter panel to manage + * its own keys. + * If this function return 0, the key will not be handled + * by ui manager. Otherwise the return will be considered + * a key code. + * + * @param panel Filter panel pointer + * @param key key code + * @return 0 if the key is handled, keycode otherwise + */ +extern int +filter_handle_key(PANEL *panel, int key); + +/** + * @brief Save form data to options + * + * This function will update the options values + * of filter fields with its new value. + * + * @param panel Filter panel pointer + */ +extern void +filter_save_options(PANEL *panel); + +#endif diff --git a/src/ui_manager.c b/src/ui_manager.c index 1c3684d..7cb7bc0 100644 --- a/src/ui_manager.c +++ b/src/ui_manager.c @@ -31,6 +31,7 @@ #include "ui_call_list.h" #include "ui_call_flow.h" #include "ui_call_raw.h" +#include "ui_filter.h" /** * @brief Warranty thread-safe ui refresh @@ -74,7 +75,13 @@ static ui_t panel_pool[] = { .create = call_raw_create, .redraw_required = call_raw_redraw_required, .draw = call_raw_draw, - .handle_key = call_raw_handle_key, } }; + .handle_key = call_raw_handle_key, }, + { + .type = FILTER_PANEL, + .panel = NULL, + .create = filter_create, + .handle_key = filter_handle_key, + .destroy = filter_destroy}, }; int init_interface() @@ -142,7 +149,7 @@ ui_get_panel(ui_t *ui) int ui_redraw_required(ui_t *ui, sip_msg_t *msg) { - int ret = -1; + int ret = 0; //! Sanity check, this should not happen if (!ui) return -1; pthread_mutex_lock(&ui->lock); @@ -171,16 +178,16 @@ ui_draw_panel(ui_t *ui) // Request the panel to draw on the scren if (ui->draw) { - if (ui->draw(ui_get_panel(ui)) == 0) { - // Update panel stack - update_panels(); - doupdate(); + if (ui->draw(ui_get_panel(ui)) != 0) { pthread_mutex_unlock(&ui->lock); - return 0; + return -1; } } + // Update panel stack + update_panels(); + doupdate(); pthread_mutex_unlock(&ui->lock); - return -1; + return 0; } void @@ -253,7 +260,7 @@ wait_for_input(ui_t *ui) int c = wgetch(win); // Check if current panel has custom bindings for that key - if (ui_handle_key(ui, c) == 0) continue; + if ((c = ui_handle_key(ui, c)) == 0) continue; // Otherwise, use standard keybindings switch (c) { @@ -265,6 +272,10 @@ wait_for_input(ui_t *ui) case 'C': set_option_value("color.callid", is_option_enabled("color.callid") ? "off" : "on"); break; + case 'p': + // Toggle capture option + toggle_option("sip.capture"); + break; case 'h': case 265: /* KEY_F1 */ ui_help(ui); diff --git a/src/ui_manager.h b/src/ui_manager.h index 8a4a058..8b13546 100644 --- a/src/ui_manager.h +++ b/src/ui_manager.h @@ -124,6 +124,8 @@ enum panel_types DETAILS_PANEL, //! Raw SIP messages ui screen RAW_PANEL, + //! Filters panel + FILTER_PANEL, }; /**