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