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