1 /**************************************************************************
2 **
3 ** sngrep - SIP Messages flow viewer
4 **
5 ** Copyright (C) 2013-2018 Ivan Alonso (Kaian)
6 ** Copyright (C) 2013-2018 Irontec SL. All rights reserved.
7 **
8 ** This program is free software: you can redistribute it and/or modify
9 ** it under the terms of the GNU General Public License as published by
10 ** the Free Software Foundation, either version 3 of the License, or
11 ** (at your option) any later version.
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **
21 ****************************************************************************/
22 /**
23 * @file ui_call_list.c
24 * @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
25 *
26 * @brief Source of functions defined in ui_call_list.h
27 *
28 */
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <regex.h>
34 #include <ctype.h>
35 #include "option.h"
36 #include "filter.h"
37 #include "capture.h"
38 #ifdef USE_EEP
39 #include "capture_eep.h"
40 #endif
41 #include "ui_manager.h"
42 #include "ui_call_list.h"
43 #include "ui_call_flow.h"
44 #include "ui_call_raw.h"
45 #include "ui_filter.h"
46 #include "ui_save.h"
47 #include "sip.h"
48
49 /**
50 * Ui Structure definition for Call List panel
51 */
52 ui_t ui_call_list = {
53 .type = PANEL_CALL_LIST,
54 .create = call_list_create,
55 .destroy = call_list_destroy,
56 .redraw = call_list_redraw,
57 .draw = call_list_draw,
58 .resize = call_list_resize,
59 .handle_key = call_list_handle_key,
60 .help = call_list_help,
61 };
62
63 void
call_list_create(ui_t * ui)64 call_list_create(ui_t *ui)
65 {
66 int i, attrid, collen;
67 call_list_info_t *info;
68 char option[80];
69 const char *field, *title;
70
71 // Create a new panel that fill all the screen
72 ui_panel_create(ui, LINES, COLS);
73
74 // Initialize Call List specific data
75 info = malloc(sizeof(call_list_info_t));
76 memset(info, 0, sizeof(call_list_info_t));
77 set_panel_userptr(ui->panel, (void*) info);
78
79 // Add configured columns
80 for (i = 0; i < SIP_ATTR_COUNT; i++) {
81 // Get column attribute name from options
82 sprintf(option, "cl.column%d", i);
83 if ((field = get_option_value(option))) {
84 if ((attrid = sip_attr_from_name(field)) == -1)
85 continue;
86 // Get column width from options
87 sprintf(option, "cl.column%d.width", i);
88 if ((collen = get_option_int_value(option)) == -1)
89 collen = sip_attr_get_width(attrid);
90 // Get column title
91 title = sip_attr_get_title(attrid);
92 // Add column to the list
93 call_list_add_column(ui, attrid, field, title, collen);
94 }
95 }
96
97 // Initialize the fields
98 info->fields[FLD_LIST_FILTER] = new_field(1, ui->width - 19, 3, 18, 0, 0);
99 info->fields[FLD_LIST_COUNT] = NULL;
100
101 // Create the form and post it
102 info->form = new_form(info->fields);
103 set_form_sub(info->form, ui->win);
104
105 // Form starts inactive
106 call_list_form_activate(ui, 0);
107 info->menu_active = 0;
108
109 // Calculate available printable area
110 info->list_win = subwin(ui->win, ui->height - 6, ui->width, 5, 0);
111 info->scroll = ui_set_scrollbar(info->list_win, SB_VERTICAL, SB_LEFT);
112
113 // Group of selected calls
114 info->group = call_group_create();
115
116 // Get current call list
117 info->cur_call = -1;
118
119 // Set autoscroll default status
120 info->autoscroll = setting_enabled(SETTING_CL_AUTOSCROLL);
121
122 // Apply initial configured filters
123 filter_method_from_setting(setting_get_value(SETTING_FILTER_METHODS));
124 filter_payload_from_setting(setting_get_value(SETTING_FILTER_PAYLOAD));
125 }
126
127 void
call_list_destroy(ui_t * ui)128 call_list_destroy(ui_t *ui)
129 {
130 call_list_info_t *info;
131
132 // Free its status data
133 if ((info = call_list_info(ui))) {
134 // Deallocate forms data
135 if (info->form) {
136 unpost_form(info->form);
137 free_form(info->form);
138 free_field(info->fields[FLD_LIST_FILTER]);
139 }
140
141 // Deallocate group data
142 call_group_destroy(info->group);
143 vector_destroy(info->dcalls);
144
145 // Deallocate panel windows
146 delwin(info->list_win);
147 sng_free(info);
148 }
149
150 ui_panel_destroy(ui);
151 }
152
153 call_list_info_t *
call_list_info(ui_t * ui)154 call_list_info(ui_t *ui)
155 {
156 return (call_list_info_t*) panel_userptr(ui->panel);
157 }
158
159 bool
call_list_redraw(ui_t * ui)160 call_list_redraw(ui_t *ui)
161 {
162 return sip_calls_has_changed();
163 }
164
165 int
call_list_resize(ui_t * ui)166 call_list_resize(ui_t *ui)
167 {
168 int maxx, maxy;
169
170 // Get panel info
171 call_list_info_t *info = call_list_info(ui);
172 // Get current screen dimensions
173 getmaxyx(stdscr, maxy, maxx);
174
175 // Change the main window size
176 wresize(ui->win, maxy, maxx);
177
178 // Store new size
179 ui->width = maxx;
180 ui->height = maxy;
181
182 // Calculate available printable area
183 wresize(info->list_win, maxy - 6, maxx); //-4
184 // Force list redraw
185 call_list_clear(ui);
186
187 return 0;
188 }
189
190 void
call_list_draw_header(ui_t * ui)191 call_list_draw_header(ui_t *ui)
192 {
193 const char *infile, *coldesc;
194 int colpos, collen, i;
195 char sortind;
196 const char *countlb;
197 const char *device, *filterexpr, *filterbpf;
198
199 // Get panel info
200 call_list_info_t *info = call_list_info(ui);
201
202 // Draw panel title
203 ui_set_title(ui, "sngrep - SIP messages flow viewer");
204
205 // Draw a Panel header lines
206 ui_clear_line(ui, 1);
207
208 // Print Open filename in Offline mode
209 if (!capture_is_online() && (infile = capture_input_file()))
210 mvwprintw(ui->win, 1, 77, "Filename: %s", infile);
211
212 mvwprintw(ui->win, 1, 2, "Current Mode: ");
213 if (capture_is_online()) {
214 wattron(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF));
215 } else {
216 wattron(ui->win, COLOR_PAIR(CP_RED_ON_DEF));
217 }
218 wprintw(ui->win, "%s ", capture_status_desc());
219
220 // Get online mode capture device
221 if ((device = capture_device()))
222 wprintw(ui->win, "[%s]", device);
223
224 #ifdef USE_EEP
225 const char *eep_port;
226 if ((eep_port = capture_eep_send_port())) {
227 wprintw(ui->win, "[H:%s]", eep_port);
228 }
229 if ((eep_port = capture_eep_listen_port())) {
230 wprintw(ui->win, "[L:%s]", eep_port);
231 }
232 #endif
233
234 wattroff(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF));
235 wattroff(ui->win, COLOR_PAIR(CP_RED_ON_DEF));
236
237 // Label for Display filter
238 mvwprintw(ui->win, 3, 2, "Display Filter: ");
239
240 mvwprintw(ui->win, 2, 2, "Match Expression: ");
241
242 wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
243 if ((filterexpr = sip_get_match_expression()))
244 wprintw(ui->win, "%s", filterexpr);
245 wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
246
247 mvwprintw(ui->win, 2, 45, "BPF Filter: ");
248 wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
249 if ((filterbpf = capture_get_bpf_filter()))
250 wprintw(ui->win, "%s", filterbpf);
251 wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
252
253 // Reverse colors on monochrome terminals
254 if (!has_colors())
255 wattron(ui->win, A_REVERSE);
256
257 // Get configured sorting options
258 sip_sort_t sort = sip_sort_options();
259
260 // Draw columns titles
261 wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
262 ui_clear_line(ui, 4);
263
264 // Draw separator line
265 if (info->menu_active) {
266 colpos = 18;
267 mvwprintw(ui->win, 4, 0, "Sort by");
268 wattron(ui->win, A_BOLD | COLOR_PAIR(CP_CYAN_ON_BLACK));
269 mvwprintw(ui->win, 4, 11, "%*s", 1, "");
270 wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
271 } else {
272 colpos = 6;
273 }
274
275 for (i = 0; i < info->columncnt; i++) {
276 // Get current column width
277 collen = info->columns[i].width;
278 // Get current column title
279 coldesc = sip_attr_get_title(info->columns[i].id);
280
281 // Check if the column will fit in the remaining space of the screen
282 if (colpos + strlen(coldesc) >= ui->width)
283 break;
284
285 // Print sort column indicator
286 if (info->columns[i].id == sort.by) {
287 wattron(ui->win, A_BOLD | COLOR_PAIR(CP_YELLOW_ON_CYAN));
288 sortind = (sort.asc) ? '^' : 'v';
289 mvwprintw(ui->win, 4, colpos, "%c%.*s", sortind, collen, coldesc);
290 wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
291 } else {
292 mvwprintw(ui->win, 4, colpos, "%.*s", collen, coldesc);
293 }
294 colpos += collen + 1;
295 }
296 // Print Autoscroll indicator
297 if (info->autoscroll)
298 mvwprintw(ui->win, 4, 0, "A");
299 wattroff(ui->win, A_BOLD | A_REVERSE | COLOR_PAIR(CP_DEF_ON_CYAN));
300
301 // Print Dialogs or Calls in label depending on calls filter
302 if (setting_enabled(SETTING_SIP_CALLS)) {
303 countlb = "Calls";
304 } else {
305 countlb = "Dialogs";
306 }
307
308 // Print calls count (also filtered)
309 sip_stats_t stats = sip_calls_stats();
310 mvwprintw(ui->win, 1, 45, "%*s", 30, "");
311 if (stats.total != stats.displayed) {
312 mvwprintw(ui->win, 1, 45, "%s: %d (%d displayed)", countlb, stats.total, stats.displayed);
313 } else {
314 mvwprintw(ui->win, 1, 45, "%s: %d", countlb, stats.total);
315 }
316
317 }
318
319 void
call_list_draw_footer(ui_t * ui)320 call_list_draw_footer(ui_t *ui)
321 {
322 const char *keybindings[] = {
323 key_action_key_str(ACTION_PREV_SCREEN), "Quit",
324 key_action_key_str(ACTION_SHOW_FLOW), "Show",
325 key_action_key_str(ACTION_SELECT), "Select",
326 key_action_key_str(ACTION_SHOW_HELP), "Help",
327 key_action_key_str(ACTION_SAVE), "Save",
328 key_action_key_str(ACTION_DISP_FILTER), "Search",
329 key_action_key_str(ACTION_SHOW_FLOW_EX), "Extended",
330 key_action_key_str(ACTION_CLEAR_CALLS), "Clear",
331 key_action_key_str(ACTION_SHOW_FILTERS), "Filter",
332 key_action_key_str(ACTION_SHOW_SETTINGS), "Settings",
333 key_action_key_str(ACTION_CLEAR_CALLS_SOFT), "Clear with Filter",
334 key_action_key_str(ACTION_SHOW_COLUMNS), "Columns"
335 };
336
337 ui_draw_bindings(ui, keybindings, 23);
338 }
339
340 void
call_list_draw_list(ui_t * ui)341 call_list_draw_list(ui_t *ui)
342 {
343 WINDOW *list_win;
344 int listh, listw, cline = 0;
345 struct sip_call *call = NULL;
346 int i, collen;
347 char coltext[SIP_ATTR_MAXLEN];
348 int colid;
349 int colpos;
350 int color;
351
352 // Get panel info
353 call_list_info_t *info = call_list_info(ui);
354
355 // Get window of call list panel
356 list_win = info->list_win;
357 getmaxyx(list_win, listh, listw);
358
359 // Store selected call
360 if (info->cur_call >= 0)
361 call = vector_item(info->dcalls, info->cur_call);
362
363 // Get the list of calls that are goint to be displayed
364 vector_destroy(info->dcalls);
365 info->dcalls = vector_copy_if(sip_calls_vector(), filter_check_call);
366
367 // If no active call, use the fist one (if exists)
368 if (info->cur_call == -1 && vector_count(info->dcalls)) {
369 info->cur_call = info->scroll.pos = 0;
370 }
371
372 // If autoscroll is enabled, select the last dialog
373 if (info->autoscroll) {
374 sip_sort_t sort = sip_sort_options();
375 if (sort.asc) {
376 call_list_move(ui, vector_count(info->dcalls) - 1);
377 } else {
378 call_list_move(ui, 0);
379 }
380 } else if (call) {
381 call_list_move(ui, vector_index(info->dcalls, call));
382 }
383
384 // Clear call list before redrawing
385 werase(list_win);
386
387 // Set the iterator position to the first call
388 vector_iter_t it = vector_iterator(info->dcalls);
389 vector_iterator_set_current(&it, info->scroll.pos - 1);
390
391 // Fill the call list
392 while ((call = vector_iterator_next(&it))) {
393 // Stop if we have reached the bottom of the list
394 if (cline == listh)
395 break;
396
397 // We only print calls with messages (In fact, all call should have msgs)
398 if (!call_msg_count(call))
399 continue;
400
401 // Show bold selected rows
402 if (call_group_exists(info->group, call))
403 wattron(list_win, A_BOLD | COLOR_PAIR(CP_DEFAULT));
404
405 // Highlight active call
406 if (info->cur_call == vector_iterator_current(&it)) {
407 wattron(list_win, COLOR_PAIR(CP_WHITE_ON_BLUE));
408 // Reverse colors on monochrome terminals
409 if (!has_colors())
410 wattron(list_win, A_REVERSE);
411 }
412 // Set current line background
413 mvwprintw(list_win, cline, 0, "%*s", listw, "");
414 // Set current line selection box
415 mvwprintw(list_win, cline, 2, call_group_exists(info->group, call) ? "[*]" : "[ ]");
416
417 // Print requested columns
418 colpos = 6;
419 for (i = 0; i < info->columncnt; i++) {
420 // Get current column id
421 colid = info->columns[i].id;
422 // Get current column width
423 collen = info->columns[i].width;
424 // Check if next column fits on window width
425 if (colpos + collen >= listw)
426 break;
427
428 // Initialize column text
429 memset(coltext, 0, sizeof(coltext));
430
431 // Get call attribute for current column
432 if (!call_get_attribute(call, colid, coltext)) {
433 colpos += collen + 1;
434 continue;
435 }
436
437 // Enable attribute color (if not current one)
438 color = 0;
439 if (info->cur_call != vector_iterator_current(&it)) {
440 if ((color = sip_attr_get_color(colid, coltext)) > 0) {
441 wattron(list_win, color);
442 }
443 }
444
445 // Add the column text to the existing columns
446 mvwprintw(list_win, cline, colpos, "%.*s", collen, coltext);
447 colpos += collen + 1;
448
449 // Disable attribute color
450 if (color > 0)
451 wattroff(list_win, color);
452 }
453 cline++;
454
455 wattroff(list_win, COLOR_PAIR(CP_DEFAULT));
456 wattroff(list_win, COLOR_PAIR(CP_DEF_ON_BLUE));
457 wattroff(list_win, A_BOLD | A_REVERSE);
458 }
459
460 // Draw scrollbar to the right
461 info->scroll.max = vector_count(info->dcalls);
462 ui_scrollbar_draw(info->scroll);
463
464 // Refresh the list
465 if (!info->menu_active)
466 wnoutrefresh(info->list_win);
467 }
468
469 int
call_list_draw(ui_t * ui)470 call_list_draw(ui_t *ui)
471 {
472 int cury, curx;
473
474 // Store cursor position
475 getyx(ui->win, cury, curx);
476
477 // Draw the header
478 call_list_draw_header(ui);
479 // Draw the footer
480 call_list_draw_footer(ui);
481 // Draw the list content
482 call_list_draw_list(ui);
483
484 // Restore cursor position
485 wmove(ui->win, cury, curx);
486
487 return 0;
488 }
489
490 void
call_list_form_activate(ui_t * ui,int active)491 call_list_form_activate(ui_t *ui, int active)
492 {
493 call_list_info_t *info = call_list_info(ui);
494
495 // Store form state
496 info->form_active = active;
497
498 if (active) {
499 set_current_field(info->form, info->fields[FLD_LIST_FILTER]);
500 // Show cursor
501 curs_set(1);
502 // Change current field background
503 set_field_back(info->fields[FLD_LIST_FILTER], A_REVERSE);
504 } else {
505 set_current_field(info->form, NULL);
506 // Hide cursor
507 curs_set(0);
508 // Change current field background
509 set_field_back(info->fields[FLD_LIST_FILTER], A_NORMAL);
510 }
511 post_form(info->form);
512 form_driver(info->form, REQ_END_LINE);
513 }
514
515 const char *
call_list_line_text(ui_t * ui,sip_call_t * call,char * text)516 call_list_line_text(ui_t *ui, sip_call_t *call, char *text)
517 {
518 int i, collen;
519 char call_attr[SIP_ATTR_MAXLEN];
520 char coltext[SIP_ATTR_MAXLEN];
521 int colid;
522
523 // Get panel info
524 call_list_info_t *info = call_list_info(ui);
525
526 // Print requested columns
527 for (i = 0; i < info->columncnt; i++) {
528
529 // Get current column id
530 colid = info->columns[i].id;
531
532 // Get current column width
533 collen = info->columns[i].width;
534
535 // Check if next column fits on window width
536 if (strlen(text) + collen >= ui->width)
537 collen = ui->width - strlen(text);
538
539 // If no space left on the screen stop processing columns
540 if (collen <= 0)
541 break;
542
543 // Initialize column text
544 memset(coltext, 0, sizeof(coltext));
545 memset(call_attr, 0, sizeof(call_attr));
546
547 // Get call attribute for current column
548 if (call_get_attribute(call, colid, call_attr)) {
549 sprintf(coltext, "%.*s", collen, call_attr);
550 }
551 // Add the column text to the existing columns
552 sprintf(text + strlen(text), "%-*s ", collen, coltext);
553 }
554
555 return text;
556 }
557
558 int
call_list_handle_key(ui_t * ui,int key)559 call_list_handle_key(ui_t *ui, int key)
560 {
561 int listh, listw,rnpag_steps = setting_get_intvalue(SETTING_CL_SCROLLSTEP);
562 call_list_info_t *info;
563 ui_t *next_ui;
564 sip_call_group_t *group;
565 int action = -1;
566 sip_call_t *call, *xcall;
567 sip_sort_t sort;
568
569 // Sanity check, this should not happen
570 if (!(info = call_list_info(ui)))
571 return -1;
572
573 // Handle form key
574 if (info->form_active)
575 return call_list_handle_form_key(ui, key);
576
577 if (info->menu_active)
578 return call_list_handle_menu_key(ui, key);
579
580 // Get window of call list panel
581 WINDOW *list_win = info->list_win;
582 getmaxyx(list_win, listh, listw);
583
584 // Check actions for this key
585 while ((action = key_find_action(key, action)) != ERR) {
586 // Check if we handle this action
587 switch (action) {
588 case ACTION_DOWN:
589 call_list_move(ui, info->cur_call + 1);
590 break;
591 case ACTION_UP:
592 call_list_move(ui, info->cur_call - 1);
593 break;
594 case ACTION_HNPAGE:
595 rnpag_steps = rnpag_steps / 2;
596 /* no break */
597 case ACTION_NPAGE:
598 // Next page => N key down strokes
599 call_list_move(ui, info->cur_call + rnpag_steps);
600 break;
601 case ACTION_HPPAGE:
602 rnpag_steps = rnpag_steps / 2;
603 /* no break */
604 case ACTION_PPAGE:
605 // Prev page => N key up strokes
606 call_list_move(ui, info->cur_call - rnpag_steps);
607 break;
608 case ACTION_BEGIN:
609 // Move to first list entry
610 call_list_move(ui, 0);
611 break;
612 case ACTION_END:
613 call_list_move(ui, vector_count(info->dcalls));
614 break;
615 case ACTION_DISP_FILTER:
616 // Activate Form
617 call_list_form_activate(ui, 1);
618 break;
619 case ACTION_SHOW_FLOW:
620 case ACTION_SHOW_FLOW_EX:
621 case ACTION_SHOW_RAW:
622 // Check we have calls in the list
623 if (info->cur_call == -1)
624 break;
625 // Create a new group of calls
626 group = call_group_clone(info->group);
627
628 // If not selected call, show current call flow
629 if (call_group_count(info->group) == 0)
630 call_group_add(group, vector_item(info->dcalls, info->cur_call));
631
632 // Add xcall to the group
633 if (action == ACTION_SHOW_FLOW_EX) {
634 call = vector_item(info->dcalls, info->cur_call);
635 if (call->xcallid != NULL && strlen(call->xcallid)) {
636 if ((xcall = sip_find_by_callid(call->xcallid))) {
637 call_group_del(group, call);
638 call_group_add(group, xcall);
639 call_group_add_calls(group, xcall->xcalls);
640 group->callid = xcall->callid;
641 }
642 } else {
643 call_group_add_calls(group, call->xcalls);
644 group->callid = call->callid;
645 }
646 }
647
648 if (action == ACTION_SHOW_RAW) {
649 // Create a Call Flow panel
650 ui_create_panel(PANEL_CALL_RAW);
651 call_raw_set_group(group);
652 } else {
653 // Display current call flow (normal or extended)
654 ui_create_panel(PANEL_CALL_FLOW);
655 call_flow_set_group(group);
656 }
657 break;
658 case ACTION_SHOW_FILTERS:
659 ui_create_panel(PANEL_FILTER);
660 break;
661 case ACTION_SHOW_COLUMNS:
662 ui_create_panel(PANEL_COLUMN_SELECT);
663 break;
664 case ACTION_SHOW_STATS:
665 ui_create_panel(PANEL_STATS);
666 break;
667 case ACTION_SAVE:
668 if (capture_sources_count() > 1) {
669 dialog_run("Saving is not possible when multiple input sources are specified.");
670 break;
671 }
672 next_ui = ui_create_panel(PANEL_SAVE);
673 save_set_group(next_ui, info->group);
674 break;
675 case ACTION_CLEAR:
676 // Clear group calls
677 vector_clear(info->group->calls);
678 break;
679 case ACTION_CLEAR_CALLS:
680 // Remove all stored calls
681 sip_calls_clear();
682 // Clear List
683 call_list_clear(ui);
684 break;
685 case ACTION_CLEAR_CALLS_SOFT:
686 // Remove stored calls, keeping the currently displayed calls
687 sip_calls_clear_soft();
688 // Clear List
689 call_list_clear(ui);
690 break;
691 case ACTION_AUTOSCROLL:
692 info->autoscroll = (info->autoscroll) ? 0 : 1;
693 break;
694 case ACTION_SHOW_SETTINGS:
695 ui_create_panel(PANEL_SETTINGS);
696 break;
697 case ACTION_SELECT:
698 call = vector_item(info->dcalls, info->cur_call);
699 if (call_group_exists(info->group, call)) {
700 call_group_del(info->group, call);
701 } else {
702 call_group_add(info->group, call);
703 }
704 break;
705 case ACTION_SORT_SWAP:
706 // Change sort order
707 sort = sip_sort_options();
708 sort.asc = (sort.asc) ? false : true;
709 sip_set_sort_options(sort);
710 break;
711 case ACTION_SORT_NEXT:
712 case ACTION_SORT_PREV:
713 call_list_select_sort_attribute(ui);
714 break;
715 case ACTION_PREV_SCREEN:
716 // Handle quit from this screen unless requested
717 if (setting_enabled(SETTING_EXITPROMPT)) {
718 if (dialog_confirm("Confirm exit", "Are you sure you want to quit?", "Yes,No") == 0) {
719 ui_destroy(ui);
720 }
721 } else {
722 ui_destroy(ui);
723 }
724 return KEY_HANDLED;
725 break;
726 default:
727 // Parse next action
728 continue;
729 }
730
731 // This panel has handled the key successfully
732 break;
733 }
734
735 // Disable autoscroll on some key pressed
736 switch(action) {
737 case ACTION_DOWN:
738 case ACTION_UP:
739 case ACTION_HNPAGE:
740 case ACTION_HPPAGE:
741 case ACTION_NPAGE:
742 case ACTION_PPAGE:
743 case ACTION_BEGIN:
744 case ACTION_END:
745 case ACTION_DISP_FILTER:
746 info->autoscroll = 0;
747 break;
748 }
749
750
751 // Return if this panel has handled or not the key
752 return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED;
753 }
754
755 int
call_list_handle_form_key(ui_t * ui,int key)756 call_list_handle_form_key(ui_t *ui, int key)
757 {
758 char *dfilter;
759 int action = -1;
760
761 // Get panel information
762 call_list_info_t *info = call_list_info(ui);
763
764 // Check actions for this key
765 while ((action = key_find_action(key, action)) != ERR) {
766 // Check if we handle this action
767 switch (action) {
768 case ACTION_PRINTABLE:
769 // If this is a normal character on input field, print it
770 form_driver(info->form, key);
771 break;
772 case ACTION_PREV_SCREEN:
773 case ACTION_NEXT_FIELD:
774 case ACTION_CONFIRM:
775 case ACTION_SELECT:
776 case ACTION_UP:
777 case ACTION_DOWN:
778 // Activate list
779 call_list_form_activate(ui, 0);
780 break;
781 case ACTION_RIGHT:
782 form_driver(info->form, REQ_RIGHT_CHAR);
783 break;
784 case ACTION_LEFT:
785 form_driver(info->form, REQ_LEFT_CHAR);
786 break;
787 case ACTION_BEGIN:
788 form_driver(info->form, REQ_BEG_LINE);
789 break;
790 case ACTION_END:
791 form_driver(info->form, REQ_END_LINE);
792 break;
793 case ACTION_CLEAR:
794 form_driver(info->form, REQ_BEG_LINE);
795 form_driver(info->form, REQ_CLR_EOL);
796 break;
797 case ACTION_DELETE:
798 form_driver(info->form, REQ_DEL_CHAR);
799 break;
800 case ACTION_BACKSPACE:
801 form_driver(info->form, REQ_DEL_PREV);
802 break;
803 default:
804 // Parse next action
805 continue;
806 }
807
808 // We've handled this key, stop checking actions
809 break;
810 }
811
812 // Filter has changed, re-apply filter to displayed calls
813 if (action == ACTION_PRINTABLE || action == ACTION_BACKSPACE ||
814 action == ACTION_DELETE || action == ACTION_CLEAR) {
815 // Updated displayed results
816 call_list_clear(ui);
817 // Reset filters on each key stroke
818 filter_reset_calls();
819 }
820
821 // Validate all input data
822 form_driver(info->form, REQ_VALIDATION);
823
824 // Store dfilter input
825 int field_len = strlen(field_buffer(info->fields[FLD_LIST_FILTER], 0));
826 dfilter = malloc(field_len + 1);
827 memset(dfilter, 0, field_len + 1);
828 strncpy(dfilter, field_buffer(info->fields[FLD_LIST_FILTER], 0), field_len);
829 // Trim any trailing spaces
830 strtrim(dfilter);
831
832 // Set display filter
833 filter_set(FILTER_CALL_LIST, strlen(dfilter) ? dfilter : NULL);
834 free(dfilter);
835
836 // Return if this panel has handled or not the key
837 return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED;
838 }
839
840 int
call_list_handle_menu_key(ui_t * ui,int key)841 call_list_handle_menu_key(ui_t *ui, int key)
842 {
843 MENU *menu;
844 int i;
845 int action = -1;
846 sip_sort_t sort;
847 enum sip_attr_id id;
848
849 // Get panel information
850 call_list_info_t *info = call_list_info(ui);
851
852 menu = info->menu;
853
854 // Check actions for this key
855 while ((action = key_find_action(key, action)) != ERR) {
856 // Check if we handle this action
857 switch (action) {
858 case ACTION_DOWN:
859 menu_driver(menu, REQ_DOWN_ITEM);
860 break;
861 case ACTION_UP:
862 menu_driver(menu, REQ_UP_ITEM);
863 break;
864 case ACTION_NPAGE:
865 menu_driver(menu, REQ_SCR_DPAGE);
866 break;
867 case ACTION_PPAGE:
868 menu_driver(menu, REQ_SCR_UPAGE);
869 break;
870 case ACTION_CONFIRM:
871 case ACTION_SELECT:
872 // Change sort attribute
873 sort = sip_sort_options();
874 id = sip_attr_from_name(item_name(current_item(info->menu)));
875 if (sort.by == id) {
876 sort.asc = (sort.asc) ? false : true;
877 } else {
878 sort.by = id;
879 }
880 sip_set_sort_options(sort);
881 /* no break */
882 case ACTION_PREV_SCREEN:
883 // Desactive sorting menu
884 info->menu_active = 0;
885
886 // Remove menu
887 unpost_menu(info->menu);
888 free_menu(info->menu);
889
890 // Remove items
891 for (i = 0; i < SIP_ATTR_COUNT; i++) {
892 if (!info->items[i])
893 break;
894 free_item(info->items[i]);
895 }
896
897 // Restore list position
898 mvderwin(info->list_win, 5, 0);
899 // Restore list window size
900 wresize(info->list_win, ui->height - 6, ui->width);
901 break;
902 default:
903 // Parse next action
904 continue;
905 }
906
907 // We've handled this key, stop checking actions
908 break;
909 }
910
911 // Return if this panel has handled or not the key
912 return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED;
913 }
914
915 int
call_list_help(ui_t * ui)916 call_list_help(ui_t *ui)
917 {
918 WINDOW *help_win;
919 int height, width;
920
921 // Create a new panel and show centered
922 height = 28;
923 width = 65;
924 help_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2);
925
926 // Set the window title
927 mvwprintw(help_win, 1, 25, "Call List Help");
928
929 // Write border and boxes around the window
930 wattron(help_win, COLOR_PAIR(CP_BLUE_ON_DEF));
931 box(help_win, 0, 0);
932 mvwhline(help_win, 2, 1, ACS_HLINE, width - 2);
933 mvwhline(help_win, 7, 1, ACS_HLINE, width - 2);
934 mvwhline(help_win, height - 3, 1, ACS_HLINE, width - 2);
935 mvwaddch(help_win, 2, 0, ACS_LTEE);
936 mvwaddch(help_win, 7, 0, ACS_LTEE);
937 mvwaddch(help_win, height - 3, 0, ACS_LTEE);
938 mvwaddch(help_win, 2, 64, ACS_RTEE);
939 mvwaddch(help_win, 7, 64, ACS_RTEE);
940 mvwaddch(help_win, height - 3, 64, ACS_RTEE);
941
942 // Set the window footer (nice blue?)
943 mvwprintw(help_win, height - 2, 20, "Press any key to continue");
944
945 // Some brief explanation abotu what window shows
946 wattron(help_win, COLOR_PAIR(CP_CYAN_ON_DEF));
947 mvwprintw(help_win, 3, 2, "This windows show the list of parsed calls from a pcap file ");
948 mvwprintw(help_win, 4, 2, "(Offline) or a live capture with libpcap functions (Online).");
949 mvwprintw(help_win, 5, 2, "You can configure the columns shown in this screen and some");
950 mvwprintw(help_win, 6, 2, "static filters using sngreprc resource file.");
951 wattroff(help_win, COLOR_PAIR(CP_CYAN_ON_DEF));
952
953 // A list of available keys in this window
954 mvwprintw(help_win, 8, 2, "Available keys:");
955 mvwprintw(help_win, 10, 2, "Esc/Q Exit sngrep.");
956 mvwprintw(help_win, 11, 2, "Enter Show selected calls message flow");
957 mvwprintw(help_win, 12, 2, "Space Select call");
958 mvwprintw(help_win, 13, 2, "F1/h Show this screen");
959 mvwprintw(help_win, 14, 2, "F2/S Save captured packages to a file");
960 mvwprintw(help_win, 15, 2, "F3// Display filtering (match string case insensitive)");
961 mvwprintw(help_win, 16, 2, "F4/X Show selected call-flow (Extended) if available");
962 mvwprintw(help_win, 17, 2, "F5/Ctrl-L Clear call list (can not be undone!)");
963 mvwprintw(help_win, 18, 2, "F6/R Show selected call messages in raw mode");
964 mvwprintw(help_win, 19, 2, "F7/F Show filter options");
965 mvwprintw(help_win, 20, 2, "F8/o Show Settings");
966 mvwprintw(help_win, 21, 2, "F10/t Select displayed columns");
967 mvwprintw(help_win, 22, 2, "i/I Set display filter to invite");
968 mvwprintw(help_win, 23, 2, "p Stop/Resume packet capture");
969
970 // Press any key to close
971 wgetch(help_win);
972 delwin(help_win);
973
974 return 0;
975 }
976
977 int
call_list_add_column(ui_t * ui,enum sip_attr_id id,const char * attr,const char * title,int width)978 call_list_add_column(ui_t *ui, enum sip_attr_id id, const char* attr,
979 const char *title, int width)
980 {
981 call_list_info_t *info;
982
983 if (!(info = call_list_info(ui)))
984 return 1;
985
986 info->columns[info->columncnt].id = id;
987 info->columns[info->columncnt].attr = attr;
988 info->columns[info->columncnt].title = title;
989 info->columns[info->columncnt].width = width;
990 info->columncnt++;
991 return 0;
992 }
993
994 void
call_list_clear(ui_t * ui)995 call_list_clear(ui_t *ui)
996 {
997 call_list_info_t *info;
998
999 // Get panel info
1000 if (!(info = call_list_info(ui)))
1001 return;
1002
1003 // Initialize structures
1004 info->scroll.pos = info->cur_call = -1;
1005 vector_clear(info->group->calls);
1006
1007 // Clear Displayed lines
1008 werase(info->list_win);
1009 }
1010
1011 void
call_list_move(ui_t * ui,int line)1012 call_list_move(ui_t *ui, int line)
1013 {
1014 call_list_info_t *info;
1015
1016 // Get panel info
1017 if (!(info = call_list_info(ui)))
1018 return;
1019
1020 // Already in this position?
1021 if (info->cur_call == line)
1022 return;
1023
1024 // Moving down or up?
1025 bool move_down = (info->cur_call < line);
1026
1027 vector_iter_t it = vector_iterator(info->dcalls);
1028 vector_iterator_set_current(&it, info->cur_call);
1029
1030 if (move_down) {
1031 while (info->cur_call < line) {
1032 // Check if there is a call below us
1033 if (!vector_iterator_next(&it))
1034 break;
1035
1036 // Increase current call position
1037 info->cur_call++;
1038
1039 // If we are out of the bottom of the displayed list
1040 // refresh it starting in the next call
1041 if (info->cur_call - info->scroll.pos == getmaxy(info->list_win)) {
1042 info->scroll.pos++;
1043 }
1044 }
1045 } else {
1046 while (info->cur_call > line) {
1047 // Check if there is a call above us
1048 if (!vector_iterator_prev(&it))
1049 break;
1050 // If we are out of the top of the displayed list
1051 // refresh it starting in the previous (in fact current) call
1052 if (info->cur_call == info->scroll.pos) {
1053 info->scroll.pos--;
1054 }
1055 // Move current call position
1056 info->cur_call--;
1057 }
1058 }
1059 }
1060
1061 void
call_list_select_sort_attribute(ui_t * ui)1062 call_list_select_sort_attribute(ui_t *ui)
1063 {
1064 call_list_info_t *info;
1065 int i;
1066
1067 // Get panel info
1068 if (!(info = call_list_info(ui)))
1069 return;
1070
1071 // Activete sorting menu
1072 info->menu_active = 1;
1073
1074 wresize(info->list_win, ui->height - 6, ui->width - 12);
1075 mvderwin(info->list_win, 5, 12);
1076
1077 // Create menu entries
1078 for (i = 0; i < info->columncnt; i++) {
1079 info->items[i] = new_item(sip_attr_get_name(info->columns[i].id), 0);
1080 }
1081 info->items[info->columncnt] = NULL;
1082 // Create the columns menu and post it
1083 info->menu = new_menu(info->items);
1084
1085 // Set main window and sub window
1086 set_menu_win(info->menu, ui->win);
1087 set_menu_sub(info->menu, derwin(ui->win, 20, 15, 5, 0));
1088 werase(menu_win(info->menu));
1089 set_menu_format(info->menu, ui->height, 1);
1090 set_menu_mark(info->menu, "");
1091 set_menu_fore(info->menu, COLOR_PAIR(CP_DEF_ON_BLUE));
1092 menu_opts_off(info->menu, O_ONEVALUE);
1093 post_menu(info->menu);
1094 }
1095