diff --git a/configure.ac b/configure.ac
index 4edaeca..7e5f6a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,11 +29,6 @@ AC_LANG(C)
#######################################################################
# Check for other REQUIRED libraries
-
-AC_CHECK_LIB([m],[ceil],[],[
- AC_MSG_ERROR([ Your glibc version does not support math functions.])
-])
-
AC_CHECK_HEADER([ncurses.h], [], [
AC_MSG_ERROR([ You need to have ncurses development files installed to compile sngrep.])
])
@@ -50,6 +45,10 @@ AC_CHECK_LIB([form], [new_form], [], [
AC_MSG_ERROR([ You need to have ncurses forms library installed to compile sngrep.])
])
+AC_CHECK_LIB([menu], [new_item], [], [
+ AC_MSG_ERROR([ You need to have ncurses menu library installed to compile sngrep.])
+])
+
AC_CHECK_LIB([pthread], [pthread_create], [], [
AC_MSG_ERROR([ You need to have libpthread installed to compile sngrep.])
])
diff --git a/src/Makefile.am b/src/Makefile.am
index c7d8f6b..89cae0c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,7 @@
bin_PROGRAMS=sngrep
sngrep_SOURCES=capture.c sip.c sip_attr.c main.c option.c group.c
-sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c ui_filter.c ui_save_pcap.c ui_save_raw.c ui_msg_diff.c
+sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c
+sngrep_SOURCES+=ui_filter.c ui_save_pcap.c ui_save_raw.c ui_msg_diff.c ui_column_select.c
if WITH_OPENSSL
sngrep_SOURCES+=capture_tls.c
diff --git a/src/sip_attr.c b/src/sip_attr.c
index 0fbdbd1..5d9714a 100644
--- a/src/sip_attr.c
+++ b/src/sip_attr.c
@@ -52,7 +52,7 @@ static sip_attr_hdr_t attrs[] =
{
.id = SIP_ATTR_SRC_HOST,
.name = "srchost",
- .desc = "Source",
+ .desc = "Source Host",
.dwidth = 16, },
{
.id = SIP_ATTR_DST,
@@ -62,7 +62,7 @@ static sip_attr_hdr_t attrs[] =
{
.id = SIP_ATTR_DST_HOST,
.name = "dsthost",
- .desc = "Destiny",
+ .desc = "Destiny Host",
.dwidth = 16, },
{
.id = SIP_ATTR_CALLID,
@@ -132,7 +132,7 @@ static sip_attr_hdr_t attrs[] =
{
.id = SIP_ATTR_CALLSTATE,
.name = "state",
- .desc = "State",
+ .desc = "Call State",
.dwidth = 10 } };
sip_attr_hdr_t *
diff --git a/src/ui_call_list.c b/src/ui_call_list.c
index ad1d106..3558f9c 100644
--- a/src/ui_call_list.c
+++ b/src/ui_call_list.c
@@ -42,7 +42,7 @@ call_list_create()
{
PANEL *panel;
WINDOW *win;
- int height, width, i, colpos, collen, attrid;
+ int height, width, i, attrid, collen;
call_list_info_t *info;
char option[80];
const char *field, *title;
@@ -104,19 +104,6 @@ call_list_create()
// Draw panel title
mvwprintw(win, 0, 0, "%*s", width, "");
mvwprintw(win, 0, (width - 45) / 2, "sngrep - SIP messages flow viewer");
-
- // Draw columns titles
- mvwprintw(win, 3, 0, "%*s", width, "");
- for (colpos = 6, i = 0; i < info->columncnt; i++) {
- // Get current column width
- collen = info->columns[i].width;
-
- // Check if the column will fit in the remaining space of the screen
- if (colpos + collen >= width)
- break;
- mvwprintw(win, 3, colpos, "%.*s", collen, sip_attr_get_description(info->columns[i].id));
- colpos += collen + 1;
- }
wattroff(win, COLOR_PAIR(CP_DEF_ON_CYAN));
// Set defualt filter text if configured
@@ -181,9 +168,11 @@ call_list_draw_footer(PANEL *panel)
"F6",
"Raw",
"F7",
- "Filter" };
+ "Filter",
+ "F10",
+ "Columns" };
- draw_keybindings(panel, keybindings, 20);
+ draw_keybindings(panel, keybindings, 22);
}
int
@@ -196,10 +185,11 @@ call_list_redraw_required(PANEL *panel, sip_msg_t *msg)
int
call_list_draw(PANEL *panel)
{
- int height, width, cline = 0;
+ int height, width, cline = 0, i, colpos, collen;
struct sip_call *call;
int callcnt;
const char *ouraddr;
+ const char *coldesc;
char linetext[256];
// Get panel info
@@ -207,14 +197,35 @@ call_list_draw(PANEL *panel)
if (!info)
return -1;
+ // Get window of call list panel
+ WINDOW *win = panel_window(panel);
+ getmaxyx(win, height, width);
+
// Update current mode information
if (!info->form_active) {
- mvwprintw(panel_window(panel), 1, 2, "Current Mode: %s %9s", get_option_value("sngrep.mode"),
+ mvwprintw(win, 1, 2, "Current Mode: %s %9s", get_option_value("sngrep.mode"),
(is_option_enabled("sip.capture") ? "" : "(Stopped)"));
}
+ // Draw columns titles
+ wattron(win, COLOR_PAIR(CP_DEF_ON_CYAN));
+ mvwprintw(win, 3, 0, "%*s", width, "");
+ for (colpos = 6, i = 0; i < info->columncnt; i++) {
+ // Get current column width
+ collen = info->columns[i].width;
+ // Get current column title
+ coldesc = sip_attr_get_description(info->columns[i].id);
+
+ // Check if the column will fit in the remaining space of the screen
+ if (colpos + strlen(coldesc) >= width)
+ break;
+ mvwprintw(win, 3, colpos, "%.*s", collen, coldesc);
+ colpos += collen + 1;
+ }
+ wattroff(win, COLOR_PAIR(CP_DEF_ON_CYAN));
+
// Get window of call list panel
- WINDOW *win = info->list_win;
+ win = info->list_win;
getmaxyx(win, height, width);
// Get available calls counter (we'll use it here a couple of times)
@@ -346,13 +357,20 @@ call_list_line_text(PANEL *panel, sip_call_t *call, char *text)
// Check if next column fits on window width
if (strlen(text) + collen >= width)
+ collen -= width - strlen(text);
+
+ // If no space left on the screen stop processing columns
+ if (collen <= 0)
break;
+ // Initialize column text
+ memset(coltext, 0, sizeof(coltext));
// Get call attribute for current column
if ((call_attr = call_get_attribute(call, colid))) {
sprintf(coltext, "%.*s", collen, call_attr);
- sprintf(text + strlen(text), "%-*s ", collen, coltext);
}
+ // Add the column text to the existing columns
+ sprintf(text + strlen(text), "%-*s ", collen, coltext);
}
return text;
@@ -479,6 +497,14 @@ call_list_handle_key(PANEL *panel, int key)
wait_for_input(next_panel);
call_list_filter_update(panel);
break;
+ case 't':
+ case 'T':
+ case KEY_F(10):
+ // Display column selection panel
+ next_panel = ui_create(ui_find_by_type(COLUMN_SELECT_PANEL));
+ wait_for_input(next_panel);
+ call_list_filter_update(panel);
+ break;
case 's':
case 'S':
case KEY_F(2):
@@ -588,7 +614,7 @@ call_list_help(PANEL *panel)
int height, width;
// Create a new panel and show centered
- height = 26;
+ height = 27;
width = 65;
help_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2);
help_panel = new_panel(help_win);
@@ -634,7 +660,8 @@ call_list_help(PANEL *panel)
mvwprintw(help_win, 19, 2, "F7/F Show filter options");
mvwprintw(help_win, 20, 2, "F8/c Turn on/off window colours");
mvwprintw(help_win, 21, 2, "F9/l Turn on/off resolved addresses");
- mvwprintw(help_win, 22, 2, "i/I Set display filter to invite");
+ mvwprintw(help_win, 22, 2, "F10/t Select displayed columns");
+ mvwprintw(help_win, 23, 2, "i/I Set display filter to invite");
// Press any key to close
wgetch(help_win);
diff --git a/src/ui_column_select.c b/src/ui_column_select.c
new file mode 100644
index 0000000..ce027ca
--- /dev/null
+++ b/src/ui_column_select.c
@@ -0,0 +1,271 @@
+/**************************************************************************
+ **
+ ** sngrep - SIP Messages flow viewer
+ **
+ ** Copyright (C) 2014,2015 Ivan Alonso (Kaian)
+ ** Copyright (C) 2014,2015 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_column_select.c
+ * @author Ivan Alonso [aka Kaian]
+ *
+ * @brief Source of functions defined in ui_column_select.h
+ *
+ */
+#include
+#include
+#include "ui_manager.h"
+#include "ui_call_list.h"
+#include "ui_column_select.h"
+
+PANEL *
+column_select_create()
+{
+ int attr_id, column;
+ PANEL *panel;
+ WINDOW *win;
+ MENU *menu;
+ int height, width;
+ column_select_info_t *info;
+
+ // Calculate window dimensions
+ height = 20;
+ width = 60;
+
+ // 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(column_select_info_t));
+ memset(info, 0, sizeof(column_select_info_t));
+
+ // Store it into panel userptr
+ set_panel_userptr(panel, (void*) info);
+
+ // Create a subwin for the menu area
+ info->menu_win = derwin(win, 10, width - 2, 7, 0);
+
+ // Initialize one field for each attribute
+ for (attr_id = 0; attr_id < SIP_ATTR_SENTINEL; attr_id++) {
+ // Create a new field for this column
+ info->items[attr_id] = new_item("[ ]", sip_attr_get_description(attr_id));
+ set_item_userptr(info->items[attr_id], (void*) sip_attr_get_name(attr_id));
+ }
+ info->items[SIP_ATTR_SENTINEL] = NULL;
+
+ // Create the columns menu and post it
+ info->menu = menu = new_menu(info->items);
+
+ // Set current enabled fields
+ call_list_info_t *list_info = (call_list_info_t*) panel_userptr(
+ ui_get_panel(ui_find_by_type(MAIN_PANEL)));
+
+ // Enable current enabled fields and move them to the top
+ for (column = 0; column < list_info->columncnt; column++) {
+ const char *desc = list_info->columns[column].title;
+ for (attr_id = 0; attr_id < item_count(menu); attr_id++) {
+ if (!strcmp(item_description(info->items[attr_id]), desc)) {
+ column_select_toggle_item(panel, info->items[attr_id]);
+ column_select_move_item(panel, info->items[attr_id], column);
+ break;
+ }
+ }
+ }
+
+ // Set main window and sub window
+ set_menu_win(menu, win);
+ set_menu_sub(menu, derwin(win, 10, width - 5, 7, 2));
+ set_menu_format(menu, 10, 1);
+ set_menu_mark(menu, "");
+ set_menu_fore(menu, COLOR_PAIR(CP_DEF_ON_BLUE));
+ menu_opts_off(menu, O_ONEVALUE);
+ post_menu(menu);
+
+ // Draw a scrollbar to the right
+ draw_vscrollbar(info->menu_win, top_row(menu), item_count(menu) - 1, 0);
+
+ // Set the window title and boxes
+ mvwprintw(win, 1, width / 2 - 14, "Call List columns selection");
+ wattron(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+ title_foot_box(panel_window(panel));
+ mvwhline(win, 6, 1, ACS_HLINE, width - 1);
+ mvwaddch(win, 6, 0, ACS_LTEE);
+ mvwaddch(win, 6, width - 1, ACS_RTEE);
+ wattroff(win, COLOR_PAIR(CP_BLUE_ON_DEF));
+
+ // Some brief explanation abotu what window shows
+ wattron(win, COLOR_PAIR(CP_CYAN_ON_DEF));
+ mvwprintw(win, 3, 2, "This windows show the list of columns displayed on Call");
+ mvwprintw(win, 4, 2, "List. You can enable/disable using Space Bar and reorder");
+ mvwprintw(win, 5, 2, "them using + and - keys.");
+ mvwprintw(win, height - 2, 12, "Press Enter when done. Esc to exit.");
+ wattroff(win, COLOR_PAIR(CP_CYAN_ON_DEF));
+
+ return panel;
+}
+
+void
+column_select_destroy(PANEL *panel)
+{
+ int i, itemcnt;
+ column_select_info_t *info = (column_select_info_t*) panel_userptr(panel);
+
+ // Get item count
+ itemcnt = item_count(info->menu);
+
+ // Unpost the menu
+ unpost_menu(info->menu);
+
+ // Free items
+ for (i = 0; i < itemcnt; i++) {
+ free_item(info->items[i]);
+ }
+}
+
+int
+column_select_handle_key(PANEL *panel, int key)
+{
+ MENU *menu;
+ ITEM *current;
+ int current_idx;
+
+ // Get panel information
+ column_select_info_t *info = (column_select_info_t*) panel_userptr(panel);
+ menu = info->menu;
+
+ current = current_item(menu);
+ current_idx = item_index(current);
+
+ switch (key) {
+ case KEY_DOWN:
+ menu_driver(menu, REQ_DOWN_ITEM);
+ break;
+ case KEY_UP:
+ menu_driver(menu, REQ_UP_ITEM);
+ break;
+ case KEY_NPAGE:
+ menu_driver(menu, REQ_SCR_DPAGE);
+ break;
+ case KEY_PPAGE:
+ menu_driver(menu, REQ_SCR_UPAGE);
+ break;
+ case ' ':
+ column_select_toggle_item(panel, current);
+ column_select_update_menu(panel);
+ break;
+ case '+':
+ column_select_move_item(panel, current, current_idx + 1);
+ column_select_update_menu(panel);
+ break;
+ case '-':
+ column_select_move_item(panel, current, current_idx - 1);
+ column_select_update_menu(panel);
+ break;
+ case 10:
+ column_select_update_columns(panel);
+ return 27;
+ default:
+ return key;
+ }
+
+ // Draw a scrollbar to the right
+ draw_vscrollbar(info->menu_win, top_row(menu), item_count(menu) - 1, 0);
+ wnoutrefresh(info->menu_win);
+
+ return 0;
+}
+
+void
+column_select_update_columns(PANEL *panel)
+{
+ int column, attr_id;
+
+ // Get panel information
+ column_select_info_t *info = (column_select_info_t*) panel_userptr(panel);
+
+ // Set enabled fields
+ PANEL *list_panel = ui_get_panel(ui_find_by_type(MAIN_PANEL));
+ call_list_info_t *list_info = (call_list_info_t*) panel_userptr(list_panel);
+
+ // Reset column count
+ list_info->columncnt = 0;
+
+ // Add all selected columns
+ for (column = 0; column < item_count(info->menu); column++) {
+ // If column is active
+ if (!strncmp(item_name(info->items[column]), "[ ]", 3))
+ continue;
+
+ // Get column attribute
+ attr_id = sip_attr_from_name(item_userptr(info->items[column]));
+ // Add a new column to the list
+ call_list_add_column(list_panel, attr_id, sip_attr_get_name(attr_id),
+ sip_attr_get_description(attr_id), sip_attr_get_width(attr_id));
+ }
+}
+
+void
+column_select_move_item(PANEL *panel, ITEM *item, int pos)
+{
+ // Get panel information
+ column_select_info_t *info = (column_select_info_t*) panel_userptr(panel);
+
+ // Check we have a valid position
+ if (pos == item_count(info->menu) || pos < 0)
+ return;
+
+ // Swap position with destiny
+ int item_pos = item_index(item);
+ info->items[item_pos] = info->items[pos];
+ info->items[item_pos]->index = item_pos;
+ info->items[pos] = item;
+ info->items[pos]->index = pos;
+}
+
+void
+column_select_toggle_item(PANEL *panel, ITEM *item)
+{
+ // Change item name
+ if (!strncmp(item_name(item), "[ ]", 3)) {
+ item->name.str = "[*]";
+ } else {
+ item->name.str = "[ ]";
+ }
+}
+
+void
+column_select_update_menu(PANEL *panel)
+{
+ // Get panel information
+ column_select_info_t *info = (column_select_info_t*) panel_userptr(panel);
+ ITEM *current = current_item(info->menu);
+ int top_idx = top_row(info->menu);
+
+ // Remove the menu from the subwindow
+ unpost_menu(info->menu);
+ // Set menu items
+ set_menu_items(info->menu, info->items);
+ // Put the menu agin into its subwindow
+ post_menu(info->menu);
+
+ // Move until the current position is set
+ set_top_row(info->menu, top_idx);
+ set_current_item(info->menu, current);
+}
diff --git a/src/ui_column_select.h b/src/ui_column_select.h
new file mode 100644
index 0000000..1cfbf7b
--- /dev/null
+++ b/src/ui_column_select.h
@@ -0,0 +1,137 @@
+/**************************************************************************
+ **
+ ** sngrep - SIP Messages flow viewer
+ **
+ ** Copyright (C) 2014,2015 Ivan Alonso (Kaian)
+ ** Copyright (C) 2014,2015 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_column_select.h
+ * @author Ivan Alonso [aka Kaian]
+ *
+ * @brief Functions to manage columns select panel
+ */
+
+#ifndef __UI_COLUMN_SELECT_H
+#define __UI_COLUMN_SELECT_H
+
+#include
+#include "ui_manager.h"
+#include "sip_attr.h"
+
+//! Sorter declaration of struct columns_select_info
+typedef struct column_select_info column_select_info_t;
+
+/**
+ * @brief Column selector panel private information
+ *
+ * This structure contains the durable data of column selection panel.
+ */
+struct column_select_info
+{
+ // Section of panel where menu is being displayed
+ WINDOW *menu_win;
+ // Columns menu
+ MENU *menu;
+ // Columns Items
+ ITEM *items[SIP_ATTR_SENTINEL + 1];
+};
+
+/**
+ * @brief Creates a new column selection panel
+ *
+ * This function allocates all required memory for
+ * displaying the column selection panel. It also draws all the
+ * static information of the panel that will never be
+ * redrawn.
+ *
+ * @return a panel pointer
+ */
+PANEL *
+column_select_create();
+
+/**
+ * @brief Destroy column selection panel
+ *
+ * This function do the final cleanups for this panel
+ */
+void
+column_select_destroy();
+
+/**
+ * @brief Manage pressed keys for column selection 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 Column selection panel pointer
+ * @param key key code
+ * @return 0 if the key is handled, keycode otherwise
+ */
+int
+column_select_handle_key(PANEL *panel, int key);
+
+/**
+ * @brief Update Call List columns
+ *
+ * This function will update the columns of Call List
+ *
+ * @param panel Column selection panel pointer
+ */
+void
+column_select_update_columns(PANEL *panel);
+
+/**
+ * @brief Move a item to a new position
+ *
+ * This function can be used to reorder the column list
+ *
+ * @param panel Column selection panel pointer
+ * @param item Menu item to be moved
+ * @param post New position in the menu
+ */
+void
+column_select_move_item(PANEL *panel, ITEM *item, int pos);
+
+/**
+ * @brief Select/Deselect a menu item
+ *
+ * This function can be used to toggle selection status of
+ * the menu item
+ *
+ * @param panel Column selection panel pointer
+ * @param item Menu item to be (de)selected
+ */
+void
+column_select_toggle_item(PANEL *panel, ITEM *item);
+
+/**
+ * @brief Update menu after a change
+ *
+ * After moving an item or updating its selection status
+ * menu must be redrawn.
+ *
+ * @param panel Column selection panel pointer
+ */
+void
+column_select_update_menu(PANEL *panel);
+
+#endif /* __UI_COLUMN_SELECT_H */
diff --git a/src/ui_manager.c b/src/ui_manager.c
index f3c6c0b..15ce5eb 100644
--- a/src/ui_manager.c
+++ b/src/ui_manager.c
@@ -38,6 +38,7 @@
#include "ui_save_pcap.h"
#include "ui_save_raw.h"
#include "ui_msg_diff.h"
+#include "ui_column_select.h"
/**
* @brief Warranty thread-safe ui refresh
@@ -109,7 +110,13 @@ static ui_t panel_pool[] =
.handle_key = msg_diff_handle_key,
.destroy = msg_diff_destroy,
.draw = msg_diff_draw,
- .help = msg_diff_help } };
+ .help = msg_diff_help },
+ {
+ .type = COLUMN_SELECT_PANEL,
+ .panel = NULL,
+ .create = column_select_create,
+ .handle_key = column_select_handle_key,
+ .destroy = column_select_destroy } };
int
init_interface()
@@ -470,7 +477,7 @@ draw_vscrollbar(WINDOW *win, int value, int max, bool left)
mvwvline(win, 0, scrollxpos, ACS_VLINE, height);
// How long the scroll will be
- scrollen = ceil(height * 1.0f / max * height);
+ scrollen = (height * 1.0f / max * height) + 0.5;
// Where will the scroll start
scrollypos = height * (value * 1.0f / max);
@@ -478,6 +485,8 @@ draw_vscrollbar(WINDOW *win, int value, int max, bool left)
// Draw the N blocks of the scrollbar
for (cline = 0; cline < scrollen; cline++)
mvwaddch(win, cline + scrollypos, scrollxpos, ACS_CKBOARD);
+
+ mvwprintw(win, 0, 0, "%d/%d [%d] %d/%d", scrollypos, height, scrollen, value, max);
}
int
diff --git a/src/ui_manager.h b/src/ui_manager.h
index c5df526..f77621a 100644
--- a/src/ui_manager.h
+++ b/src/ui_manager.h
@@ -124,6 +124,8 @@ enum panel_types
SAVE_RAW_PANEL,
//! Message comprare
MSG_DIFF_PANEL,
+ //! Column selector panel
+ COLUMN_SELECT_PANEL,
};
/**