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,
};
/**