From Anders on -dev:
[obnox/wireshark/wip.git] / gtk / iax2_analysis.c
1 /* iax2_analysis.c
2  * IAX2 analysis addition for Wireshark
3  *
4  * $Id$
5  *
6  * based on rtp_analysis.c
7  * Copyright 2003, Alcatel Business Systems
8  * By Lars Ruoff <lars.ruoff@gmx.net>
9  *
10  * based on tap_rtp.c
11  * Copyright 2003, Iskratel, Ltd, Kranj
12  * By Miha Jemec <m.jemec@iskratel.si>
13  *
14  * Graph. Copyright 2004, Verso Technology
15  * By Alejandro Vaquero <alejandro.vaquero@verso.com>
16  * Based on io_stat.c by Ronnie Sahlberg
17  *
18  * Wireshark - Network traffic analyzer
19  * By Gerald Combs <gerald@wireshark.org>
20  * Copyright 1998 Gerald Combs
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <stdio.h>
42 #include <math.h>
43 #include <string.h>
44 #include <locale.h>
45
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49
50 #ifdef HAVE_FCNTL_H
51 #include <fcntl.h>
52 #endif
53
54 #include <gtk/gtk.h>
55
56 #include <epan/epan_dissect.h>
57 #include <epan/filesystem.h>
58 #include <epan/pint.h>
59 #include <epan/tap.h>
60 #include <epan/tap-voip.h>
61 #include <epan/dissectors/packet-iax2.h>
62 #include <epan/iax2_codec_type.h>
63 #include <epan/addr_resolv.h>
64 #include <epan/stat_cmd_args.h>
65 #include <epan/strutil.h>
66
67 #include "../util.h"
68 #include "../g711.h"
69 #include "../alert_box.h"
70 #include "../simple_dialog.h"
71 #include "../stat_menu.h"
72 #include "../progress_dlg.h"
73 #include "../tempfile.h"
74 #include <wsutil/file_util.h>
75
76 #include "gtk/gtkglobals.h"
77 #include "gtk/dlg_utils.h"
78 #include "gtk/file_dlg.h"
79 #include "gtk/gui_utils.h"
80 #include "gtk/gui_stat_menu.h"
81 #include "gtk/main.h"
82 #include "gtk/rtp_analysis.h"
83 #include "gtk/iax2_analysis.h"
84 #include "gtk/rtp_stream.h"
85 #include "gtk/rtp_stream_dlg.h"
86 #include "gtk/utf8_entities.h"
87
88 #include "gtk/old-gtk-compat.h"
89
90 enum
91 {
92         PACKET_COLUMN,
93         DELTA_COLUMN,
94         JITTER_COLUMN,
95         IPBW_COLUMN,
96         STATUS_COLUMN,
97         DATE_COLUMN,
98         LENGTH_COLUMN,
99         FOREGROUND_COLOR_COL,
100         BACKGROUND_COLOR_COL,
101         N_COLUMN /* The number of columns */
102 };
103
104 /****************************************************************************/
105 #define NUM_COLS 7
106 #define NUM_GRAPH_ITEMS 100000
107 #define MAX_YSCALE 16
108 #define AUTO_MAX_YSCALE_INDEX 0
109 #define AUTO_MAX_YSCALE 0
110 #define MAX_GRAPHS 4
111 #define GRAPH_FWD_JITTER 0
112 #define GRAPH_FWD_DIFF 1
113 #define GRAPH_REV_JITTER 2
114 #define GRAPH_REV_DIFF 3
115 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
116
117 #define MAX_PIXELS_PER_TICK 4
118 #define DEFAULT_PIXELS_PER_TICK_INDEX 1
119 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
120 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
121 /* unit is in ms */
122 #define MAX_TICK_VALUES 5
123 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
124 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
125 typedef struct _dialog_graph_graph_item_t {
126         guint32 value;
127         guint32 flags;
128 } dialog_graph_graph_item_t;
129
130 typedef struct _dialog_graph_graph_t {
131         struct _user_data_t *ud;
132         dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
133         int plot_style;
134         gboolean display;
135         GtkWidget *display_button;
136         int hf_index;
137         GdkColor color;
138         gchar title[100];
139 } dialog_graph_graph_t;
140
141
142 typedef struct _dialog_graph_t {
143         gboolean needs_redraw;
144         gint32 interval_index; /* index into tick_interval_values array */
145         gint32 interval;       /* measurement interval in ms */
146         guint32 last_interval;
147         guint32 max_interval;  /* XXX max_interval and num_items are redundant */
148         guint32 num_items;
149         struct _dialog_graph_graph_t graph[MAX_GRAPHS];
150         GtkWidget *window;
151         GtkWidget *draw_area;
152 #if GTK_CHECK_VERSION(2,22,0)
153         cairo_surface_t *surface;
154 #else
155         GdkPixmap *pixmap;
156 #endif
157         GtkAdjustment *scrollbar_adjustment;
158         GtkWidget *scrollbar;
159         int surface_width;
160         int surface_height;
161         int pixels_per_tick_index; /* index into pixels_per_tick array */
162         int pixels_per_tick;
163         int max_y_units_index;     /* index into yscale_max array      */
164         int max_y_units;
165         double start_time;
166 } dialog_graph_t;
167
168 typedef struct _dialog_data_t {
169         GtkWidget *window;
170         GtkWidget *list_fwd;
171         GtkTreeIter  iter;
172         GtkWidget *list_rev;
173         GtkWidget *label_stats_fwd;
174         GtkWidget *label_stats_rev;
175         GtkWidget *selected_list;
176         guint   number_of_nok;
177         GtkTreeSelection *selected_list_sel;
178         gint selected_list_row;
179         GtkWidget *notebook;
180         GtkWidget *save_voice_as_w;
181         GtkWidget *save_csv_as_w;
182         gint notebook_signal_id;
183         dialog_graph_t dialog_graph;
184 } dialog_data_t;
185
186 #define OK_TEXT "[ Ok ]"
187
188 /* type of error when saving voice in a file didn't succeed */
189 typedef enum {
190         TAP_RTP_WRONG_CODEC,
191         TAP_RTP_WRONG_LENGTH,
192         TAP_RTP_PADDING_ERROR,
193         TAP_RTP_SHORT_FRAME,
194         TAP_RTP_FILE_OPEN_ERROR,
195         TAP_RTP_FILE_WRITE_ERROR,
196         TAP_RTP_NO_DATA
197 } error_type_t;
198
199 typedef struct _tap_iax2_save_info_t {
200         FILE *fp;
201         guint32 count;
202         error_type_t error_type;
203         gboolean saved;
204 } tap_iax2_save_info_t;
205
206
207 /* structure that holds the information about the forward and reversed direction */
208 struct _info_direction {
209         tap_iax2_stat_t statinfo;
210         tap_iax2_save_info_t saveinfo;
211 };
212
213 #define SILENCE_PCMU    (guint8)0xFF
214 #define SILENCE_PCMA    (guint8)0x55
215
216 /* structure that holds general information about the connection
217 * and structures for both directions */
218 typedef struct _user_data_t {
219         /* tap associated data*/
220         address ip_src_fwd;
221         guint16 port_src_fwd;
222         address ip_dst_fwd;
223         guint16 port_dst_fwd;
224         address ip_src_rev;
225         guint16 port_src_rev;
226         address ip_dst_rev;
227         guint16 port_dst_rev;
228
229         struct _info_direction forward;
230         struct _info_direction reversed;
231
232         char *f_tempname;
233         char *r_tempname;
234
235         /* dialog associated data */
236         dialog_data_t dlg;
237
238 } user_data_t;
239
240
241 /* Column titles. */
242 static const gchar *titles[7] =  {
243         "Packet",
244         "Delta (ms)",
245         "Jitter (ms)",
246         "IP BW (kbps)",
247         "Status",
248         "Date",
249         "Length"
250 };
251
252 #define SAVE_FORWARD_DIRECTION_MASK 0x01
253 #define SAVE_REVERSE_DIRECTION_MASK 0x02
254 #define SAVE_BOTH_DIRECTION_MASK    (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
255
256 #define SAVE_NONE_FORMAT 0
257 #define SAVE_WAV_FORMAT  1
258 #define SAVE_AU_FORMAT   2
259 #define SAVE_SW_FORMAT   3
260 #define SAVE_RAW_FORMAT  4
261
262
263 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data);
264 /****************************************************************************/
265 static void enable_graph(dialog_graph_graph_t *dgg)
266 {
267
268         dgg->display=TRUE;
269
270 }
271
272 static void dialog_graph_reset(user_data_t* user_data);
273
274
275
276 /****************************************************************************/
277 /* TAP FUNCTIONS */
278
279 /****************************************************************************/
280 /* when there is a [re]reading of packet's */
281 static void
282 iax2_reset(void *user_data_arg)
283 {
284         user_data_t *user_data = user_data_arg;
285         user_data->forward.statinfo.first_packet = TRUE;
286         user_data->reversed.statinfo.first_packet = TRUE;
287         user_data->forward.statinfo.max_delta = 0;
288         user_data->reversed.statinfo.max_delta = 0;
289         user_data->forward.statinfo.max_jitter = 0;
290         user_data->reversed.statinfo.max_jitter = 0;
291         user_data->forward.statinfo.mean_jitter = 0;
292         user_data->reversed.statinfo.mean_jitter = 0;
293         user_data->forward.statinfo.delta = 0;
294         user_data->reversed.statinfo.delta = 0;
295         user_data->forward.statinfo.diff = 0;
296         user_data->reversed.statinfo.diff = 0;
297         user_data->forward.statinfo.jitter = 0;
298         user_data->reversed.statinfo.jitter = 0;
299         user_data->forward.statinfo.bandwidth = 0;
300         user_data->reversed.statinfo.bandwidth = 0;
301         user_data->forward.statinfo.total_bytes = 0;
302         user_data->reversed.statinfo.total_bytes = 0;
303         user_data->forward.statinfo.bw_start_index = 0;
304         user_data->reversed.statinfo.bw_start_index = 0;
305         user_data->forward.statinfo.bw_index = 0;
306         user_data->reversed.statinfo.bw_index = 0;
307         user_data->forward.statinfo.timestamp = 0;
308         user_data->reversed.statinfo.timestamp = 0;
309         user_data->forward.statinfo.max_nr = 0;
310         user_data->reversed.statinfo.max_nr = 0;
311         user_data->forward.statinfo.total_nr = 0;
312         user_data->reversed.statinfo.total_nr = 0;
313         user_data->forward.statinfo.sequence = 0;
314         user_data->reversed.statinfo.sequence = 0;
315         user_data->forward.statinfo.start_seq_nr = 0;
316         user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
317         user_data->forward.statinfo.stop_seq_nr = 0;
318         user_data->reversed.statinfo.stop_seq_nr = 0;
319         user_data->forward.statinfo.cycles = 0;
320         user_data->reversed.statinfo.cycles = 0;
321         user_data->forward.statinfo.under = FALSE;
322         user_data->reversed.statinfo.under = FALSE;
323         user_data->forward.statinfo.start_time = 0;
324         user_data->reversed.statinfo.start_time = 0;
325         user_data->forward.statinfo.time = 0;
326         user_data->reversed.statinfo.time = 0;
327         user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
328         user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
329
330         user_data->forward.saveinfo.count = 0;
331         user_data->reversed.saveinfo.count = 0;
332         user_data->forward.saveinfo.saved = FALSE;
333         user_data->reversed.saveinfo.saved = FALSE;
334
335         /* clear the dialog box lists */
336         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
337         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
338
339         /* reset graph info */
340         dialog_graph_reset(user_data);
341
342
343         /* XXX check for error at fclose? */
344         if (user_data->forward.saveinfo.fp != NULL)
345                 fclose(user_data->forward.saveinfo.fp);
346         if (user_data->reversed.saveinfo.fp != NULL)
347                 fclose(user_data->reversed.saveinfo.fp);
348         user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
349         if (user_data->forward.saveinfo.fp == NULL)
350                 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
351         user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
352         if (user_data->reversed.saveinfo.fp == NULL)
353                 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
354         return;
355 }
356
357 /****************************************************************************/
358 static gboolean iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
359 {
360         dialog_graph_graph_item_t *it;
361         guint32 idx;
362         double rtp_time;
363
364         /* we sometimes get called when dgg is disabled.
365         this is a bug since the tap listener should be removed first */
366         if(!dgg->display){
367                 return FALSE;
368         }
369
370         dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
371
372         /*
373         * Find which interval this is supposed to go in and store the
374         * interval index as idx
375         */
376         if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
377                 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
378         }
379         rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
380         if(rtp_time<0){
381                 return FALSE;
382         }
383         idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
384
385         /* some sanity checks */
386         if(idx>=NUM_GRAPH_ITEMS){
387                 return FALSE;
388         }
389
390         /* update num_items */
391         if(idx > dgg->ud->dlg.dialog_graph.num_items){
392                 dgg->ud->dlg.dialog_graph.num_items=idx;
393                 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
394         }
395
396         /*
397         * Find the appropriate dialog_graph_graph_item_t structure
398         */
399         it=&dgg->items[idx];
400
401         /*
402         * Use the max value to highlight RTP problems
403         */
404         if (value > it->value) {
405                 it->value=value;
406         }
407         it->flags = it->flags | statinfo->flags;
408
409         return TRUE;
410 }
411
412 /****************************************************************************/
413 /* here we can redraw the output */
414 /* not used yet */
415 static void iax2_draw(void *prs _U_)
416 {
417         return;
418 }
419
420 /* forward declarations */
421 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
422                         double delta, double jitter, double bandwidth, gchar *status,
423                         gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
424
425 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
426         tap_iax2_stat_t *statinfo, packet_info *pinfo,
427         const struct _iax2_info_t *iax2info);
428
429 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
430                                     tap_iax2_stat_t *statinfo,
431                                     packet_info *pinfo,
432                                     const struct _iax2_info_t *iax2info);
433
434
435 /****************************************************************************/
436 /* whenever a IAX2 packet is seen by the tap listener */
437 static gboolean iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
438 {
439         user_data_t *user_data = user_data_arg;
440         const struct _iax2_info_t *iax2info = iax2info_arg;
441
442         /* we ignore packets that are not displayed */
443         if (pinfo->fd->flags.passed_dfilter == 0)
444                 return FALSE;
445
446         /* we ignore packets that carry no data */
447         if (iax2info->payload_len == 0)
448                 return FALSE;
449
450         /* is it the forward direction?  */
451         else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
452                  && user_data->port_src_fwd == pinfo->srcport
453                  && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
454                  && user_data->port_dst_fwd == pinfo->destport)  {
455                 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
456                 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
457                                       &(user_data->forward.statinfo),
458                                       pinfo,
459                                       (guint32)(user_data->forward.statinfo.jitter*1000000));
460                 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
461                                       &(user_data->forward.statinfo),
462                                       pinfo,
463                                       (guint32)(user_data->forward.statinfo.diff*1000000));
464                 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
465                                      &(user_data->forward.statinfo), pinfo, iax2info);
466                 iax2_packet_save_payload(&(user_data->forward.saveinfo),
467                                          &(user_data->forward.statinfo), pinfo, iax2info);
468         }
469         /* is it the reversed direction? */
470         else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
471                  && user_data->port_src_rev == pinfo->srcport
472                  && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
473                  && user_data->port_dst_rev == pinfo->destport)  {
474                 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
475                 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
476                                       &(user_data->reversed.statinfo),
477                                       pinfo,
478                                       (guint32)(user_data->reversed.statinfo.jitter*1000000));
479                 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
480                                       &(user_data->reversed.statinfo),
481                                       pinfo,
482                                       (guint32)(user_data->reversed.statinfo.diff*1000000));
483                 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
484                                      &(user_data->reversed.statinfo), pinfo, iax2info);
485                 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
486                                          &(user_data->reversed.statinfo), pinfo, iax2info);
487         }
488
489         return FALSE;
490 }
491
492 /****************************************************************************/
493 /* This comes from tap-rtp-common.c */
494 /****************************************************************************/
495
496 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
497                         packet_info *pinfo,
498                         const struct _iax2_info_t *iax2info)
499 {
500         double current_time;
501         double current_jitter;
502         double current_diff;
503
504         statinfo->flags = 0;
505         /* check payload type */
506         if (iax2info->ftype == AST_FRAME_VOICE) {
507                 if (iax2info->csub != statinfo->pt)
508                         statinfo->flags |= STAT_FLAG_PT_CHANGE;
509                 statinfo->pt = iax2info->csub;
510         }
511
512         /* store the current time and calculate the current jitter */
513         current_time = nstime_to_sec(&pinfo->fd->rel_ts);
514         current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
515         current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
516         statinfo->delta = current_time-(statinfo->time);
517         statinfo->jitter = current_jitter;
518         statinfo->diff = current_diff;
519
520         /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
521         statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
522         statinfo->bw_history[statinfo->bw_index].time = current_time;
523         /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
524         while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
525                 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
526                 statinfo->bw_start_index++;
527                 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
528         };
529         statinfo->total_bytes += iax2info->payload_len + 24;
530         statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
531         statinfo->bw_index++;
532         if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
533
534
535         /*  is this the first packet we got in this direction? */
536         if (statinfo->first_packet) {
537                 statinfo->start_seq_nr = 0;
538                 statinfo->start_time = current_time;
539                 statinfo->delta = 0;
540                 statinfo->jitter = 0;
541                 statinfo->diff = 0;
542                 statinfo->flags |= STAT_FLAG_FIRST;
543                 statinfo->first_packet = FALSE;
544         }
545         /* is it a regular packet? */
546         if (!(statinfo->flags & STAT_FLAG_FIRST)
547                 && !(statinfo->flags & STAT_FLAG_MARKER)
548                 && !(statinfo->flags & STAT_FLAG_PT_CN)
549                 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
550                 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
551                 /* include it in maximum delta calculation */
552                 if (statinfo->delta > statinfo->max_delta) {
553                         statinfo->max_delta = statinfo->delta;
554                         statinfo->max_nr = pinfo->fd->num;
555                 }
556                 /* maximum and mean jitter calculation */
557                 if (statinfo->jitter > statinfo->max_jitter) {
558                         statinfo->max_jitter = statinfo->jitter;
559                 }
560                 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
561         }
562         /* regular payload change? (CN ignored) */
563         if (!(statinfo->flags & STAT_FLAG_FIRST)
564                 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
565                 if ((statinfo->pt != statinfo->reg_pt)
566                         && (statinfo->reg_pt != PT_UNDEFINED)) {
567                         statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
568                 }
569         }
570
571         /* set regular payload*/
572         if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
573                 statinfo->reg_pt = statinfo->pt;
574         }
575
576         /* TODO: lost packets / duplicated:  we should infer this from timestamp... */
577         statinfo->time = current_time;
578         statinfo->timestamp = iax2info->timestamp; /* millisecs */
579         statinfo->stop_seq_nr = 0;
580         statinfo->total_nr++;
581
582         return 0;
583 }
584
585
586 #if 0
587 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
588 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
589 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
590 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
591 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
592 #endif
593
594 /****************************************************************************/
595 /* adds statistics information from the packet to the list */
596 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
597         tap_iax2_stat_t *statinfo, packet_info *pinfo,
598         const struct _iax2_info_t *iax2info _U_)
599 {
600         guint16 msecs;
601         gchar timeStr[32];
602         struct tm *tm_tmp;
603         time_t then;
604         gchar status[40];
605         /* GdkColor color = COLOR_DEFAULT; */
606         gchar color_str[14];
607         then = pinfo->fd->abs_ts.secs;
608         msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
609         tm_tmp = localtime(&then);
610         g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
611                 tm_tmp->tm_mon + 1,
612                 tm_tmp->tm_mday,
613                 tm_tmp->tm_year + 1900,
614                 tm_tmp->tm_hour,
615                 tm_tmp->tm_min,
616                 tm_tmp->tm_sec,
617                 msecs);
618
619         /* Default to using black on white text if nothing below overrides it */
620         g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
621
622         if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
623                 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
624                 /* color = COLOR_ERROR; */
625                 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
626         }
627         else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
628                 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
629                 /* color = COLOR_WARNING; */
630                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
631         }
632         else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
633                 g_snprintf(status,sizeof(status),"Incorrect timestamp");
634                 /* color = COLOR_WARNING; */
635                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
636         }
637         else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
638                 &&  !(statinfo->flags & STAT_FLAG_FIRST)
639                 &&  !(statinfo->flags & STAT_FLAG_PT_CN)
640                 &&  (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
641                 &&  !(statinfo->flags & STAT_FLAG_MARKER)) {
642                 g_snprintf(status,sizeof(status),"Marker missing?");
643                 /* color = COLOR_WARNING; */
644                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
645         }
646         else {
647                 if (statinfo->flags & STAT_FLAG_MARKER) {
648                         /* color = COLOR_WARNING; */
649                         g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
650                 }
651                 g_snprintf(status,sizeof(status),OK_TEXT);
652         }
653         /*  is this the first packet we got in this direction? */
654         if (statinfo->flags & STAT_FLAG_FIRST) {
655                 add_to_list(list, user_data,
656                         pinfo->fd->num,
657                         0,
658                         0,
659                         statinfo->bandwidth,
660                         status,
661                         timeStr, pinfo->fd->pkt_len,
662                         color_str,
663                         statinfo->flags);
664         }
665         else {
666                 add_to_list(list, user_data,
667                         pinfo->fd->num,
668                         statinfo->delta*1000,
669                         statinfo->jitter*1000,
670                         statinfo->bandwidth,
671                         status,
672                         timeStr, pinfo->fd->pkt_len,
673                         color_str,
674                         statinfo->flags);
675         }
676         return 0;
677 }
678
679 #define MAX_SILENCE_TICKS 1000000
680 /****************************************************************************/
681 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
682                                     tap_iax2_stat_t *statinfo,
683                                     packet_info *pinfo,
684                                     const struct _iax2_info_t *iax2info)
685 {
686         const guint8 *data;
687         size_t nchars;
688
689         /*  is this the first packet we got in this direction? */
690         if (statinfo->flags & STAT_FLAG_FIRST) {
691                 if (saveinfo->fp == NULL) {
692                         saveinfo->saved = FALSE;
693                         saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
694                 }
695                 else
696                         saveinfo->saved = TRUE;
697         }
698
699         /* save the voice information */
700         /* if there was already an error, we quit */
701         if (saveinfo->saved == FALSE)
702                 return;
703
704         /* if the captured length and packet length aren't equal, we quit */
705         if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
706                 saveinfo->saved = FALSE;
707                 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
708                 return;
709         }
710
711         if (iax2info->payload_len > 0) {
712                 data = iax2info->payload_data;
713                 nchars = fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
714                 if (nchars != iax2info->payload_len) {
715                         /* Write error or short write */
716                         saveinfo->saved = FALSE;
717                         saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
718                         return;
719                 }
720                 saveinfo->count+=iax2info->payload_len;
721
722                 if (fflush(saveinfo->fp) == EOF) {
723                         /* Write error */
724                         saveinfo->saved = FALSE;
725                         saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
726                         return;
727                 }
728                 saveinfo->saved = TRUE;
729                 return;
730         }
731
732         return;
733 }
734
735
736 /****************************************************************************/
737 /* CALLBACKS */
738
739 /****************************************************************************/
740
741 /****************************************************************************/
742 /* close the dialog window and remove the tap listener */
743 static void on_iax2_window_destroy(GtkWidget *win _U_, gpointer data)
744 {
745         user_data_t *user_data = data;
746
747         /* remove tap listener */
748         protect_thread_critical_region();
749         remove_tap_listener(user_data);
750         unprotect_thread_critical_region();
751
752         /* close and remove temporary files */
753         if (user_data->forward.saveinfo.fp != NULL)
754                 fclose(user_data->forward.saveinfo.fp);
755         if (user_data->reversed.saveinfo.fp != NULL)
756                 fclose(user_data->reversed.saveinfo.fp);
757         /*XXX: test for error **/
758         ws_remove(user_data->f_tempname);
759         ws_remove(user_data->r_tempname);
760
761 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
762         /* destroy save_csv_as window if open */
763         if (user_data->dlg.save_csv_as_w != NULL)
764                 window_destroy(user_data->dlg.save_csv_as_w);
765
766         /* destroy save_voice_as window if open */
767         if (user_data->dlg.save_voice_as_w != NULL)
768                 window_destroy(user_data->dlg.save_voice_as_w);
769 #endif
770         /* destroy graph window if open */
771         if (user_data->dlg.dialog_graph.window != NULL)
772                 window_destroy(user_data->dlg.dialog_graph.window);
773
774
775         /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
776         /* XXX: Is this still true for GTK2 ???  */
777         g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
778
779         g_free(user_data->f_tempname);
780         g_free(user_data->r_tempname);
781         g_free(user_data);
782 }
783
784
785 /****************************************************************************/
786 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
787                                     gpointer *page _U_,
788                                     gint page_num,
789                                     gpointer data)
790 {
791         user_data_t *user_data = data;
792
793         user_data->dlg.selected_list =
794                 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
795
796         user_data->dlg.selected_list_row = 0;
797 }
798
799 /****************************************************************************/
800 static void on_list_select_row(GtkTreeSelection *selection,
801                                 gpointer data)
802 {
803         user_data_t *user_data = data;
804
805         user_data->dlg.selected_list_sel = selection;
806 }
807
808
809 /****************************************************************************/
810 static void dialog_graph_set_title(user_data_t* user_data)
811 {
812         char    *title;
813
814         if (!user_data->dlg.dialog_graph.window){
815                 return;
816         }
817         title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u   Reverse: %s:%u to %s:%u",
818                                 get_addr_name(&(user_data->ip_src_fwd)),
819                                 user_data->port_src_fwd,
820                                 get_addr_name(&(user_data->ip_dst_fwd)),
821                                 user_data->port_dst_fwd,
822                                 get_addr_name(&(user_data->ip_src_rev)),
823                                 user_data->port_src_rev,
824                                 get_addr_name(&(user_data->ip_dst_rev)),
825                                 user_data->port_dst_rev);
826
827         gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
828         g_free(title);
829
830 }
831
832
833 /****************************************************************************/
834 static void dialog_graph_reset(user_data_t* user_data)
835 {
836         int i, j;
837
838         user_data->dlg.dialog_graph.needs_redraw=TRUE;
839         for(i=0;i<MAX_GRAPHS;i++){
840                 for(j=0;j<NUM_GRAPH_ITEMS;j++){
841                         dialog_graph_graph_item_t *dggi;
842                         dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
843                         dggi->value=0;
844                         dggi->flags=0;
845                 }
846         }
847         user_data->dlg.dialog_graph.last_interval=0xffffffff;
848         user_data->dlg.dialog_graph.max_interval=0;
849         user_data->dlg.dialog_graph.num_items=0;
850
851         /* create the color titles near the filter buttons */
852         for(i=0;i<MAX_GRAPHS;i++){
853                 /* it is forward */
854                 if (i<2){
855                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
856                                    sizeof (user_data->dlg.dialog_graph.graph[0].title),
857                                    "%s: %s:%u to %s:%u",
858                         graph_descr[i],
859                         get_addr_name(&(user_data->ip_src_fwd)),
860                         user_data->port_src_fwd,
861                         get_addr_name(&(user_data->ip_dst_fwd)),
862                         user_data->port_dst_fwd);
863                 /* it is reverse */
864                 } else {
865                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
866                                    sizeof(user_data->dlg.dialog_graph.graph[0].title),
867                                    "%s: %s:%u to %s:%u",
868                         graph_descr[i],
869                         get_addr_name(&(user_data->ip_src_rev)),
870                         user_data->port_src_rev,
871                         get_addr_name(&(user_data->ip_dst_rev)),
872                         user_data->port_dst_rev);
873                 }
874         }
875
876         dialog_graph_set_title(user_data);
877 }
878
879 /****************************************************************************/
880 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
881 {
882         dialog_graph_graph_item_t *it;
883
884         it=&dgg->items[idx];
885
886         return it->value;
887 }
888
889 /****************************************************************************/
890 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
891 {
892         if(t>=10000000){
893                 g_snprintf(buf, buf_len, "%ds",t/1000000);
894         } else if(t>=1000000){
895                 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
896         } else if(t>=10000){
897                 g_snprintf(buf, buf_len, "%dms",t/1000);
898         } else if(t>=1000){
899                 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
900         } else {
901                 g_snprintf(buf, buf_len, "%dus",t);
902         }
903 }
904
905 /****************************************************************************/
906 static void dialog_graph_draw(user_data_t* user_data)
907 {
908         int i, lwidth;
909         guint32 last_interval, first_interval, interval_delta, delta_multiplier;
910         gint32 current_interval;
911         guint32 left_x_border;
912         guint32 right_x_border;
913         guint32 top_y_border;
914         guint32 bottom_y_border;
915         PangoLayout  *layout;
916         int label_width, label_height;
917         int label_width_mid, label_height_mid;
918         guint32 draw_width, draw_height;
919         char label_string[15];
920         GtkAllocation widget_alloc;
921         cairo_t *cr;
922
923         /* new variables */
924         guint32 num_time_intervals;
925         guint32 max_value;              /* max value of seen data */
926         guint32 max_y;                  /* max value of the Y scale */
927
928         if(!user_data->dlg.dialog_graph.needs_redraw){
929                 return;
930         }
931         user_data->dlg.dialog_graph.needs_redraw=FALSE;
932
933         /*
934          * Find the length of the intervals we have data for
935          * so we know how large arrays we need to malloc()
936          */
937         num_time_intervals=user_data->dlg.dialog_graph.num_items;
938         /* if there isnt anything to do, just return */
939         if(num_time_intervals==0){
940                 return;
941         }
942         num_time_intervals+=1;
943         /* XXX move this check to _packet() */
944         if(num_time_intervals>NUM_GRAPH_ITEMS){
945                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
946                 return;
947         }
948
949         /*
950          * find the max value so we can autoscale the y axis
951          */
952         max_value=0;
953         for(i=0;i<MAX_GRAPHS;i++){
954                 int idx;
955
956                 if(!user_data->dlg.dialog_graph.graph[i].display){
957                         continue;
958                 }
959                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
960                         guint32 val;
961
962                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
963
964                         /* keep track of the max value we have encountered */
965                         if(val>max_value){
966                                 max_value=val;
967                         }
968                 }
969         }
970
971         /*
972          * Clear out old plot
973          */
974 #if GTK_CHECK_VERSION(2,22,0)
975         cr = cairo_create (user_data->dlg.dialog_graph.surface);
976 #else
977         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
978 #endif
979         cairo_set_source_rgb (cr, 1, 1, 1);
980         gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
981         cairo_rectangle (cr,
982                 0,
983                 0,
984                 widget_alloc.width,
985                 widget_alloc.height);
986         cairo_fill (cr);
987         cairo_destroy (cr);
988
989
990         /*
991          * Calculate the y scale we should use
992          */
993         if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
994                 max_y=yscale_max[MAX_YSCALE-1];
995                 for(i=MAX_YSCALE-1;i>0;i--){
996                         if(max_value<yscale_max[i]){
997                                 max_y=yscale_max[i];
998                         }
999                 }
1000         } else {
1001                 /* the user had specified an explicit y scale to use */
1002                 max_y=user_data->dlg.dialog_graph.max_y_units;
1003         }
1004
1005         /*
1006          * Calculate size of borders surrounding the plot
1007          * The border on the right side needs to be adjusted depending
1008          * on the width of the text labels.
1009          */
1010         print_time_scale_string(label_string, sizeof(label_string), max_y);
1011         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1012         pango_layout_get_pixel_size(layout, &label_width, &label_height);
1013         print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1014         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1015         pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1016         if (label_width_mid > label_width) {
1017                 label_width = label_width_mid;
1018                 label_height = label_height_mid;
1019         }
1020
1021         left_x_border=10;
1022         right_x_border=label_width+20;
1023         top_y_border=10;
1024         bottom_y_border=label_height+20;
1025
1026
1027         /*
1028          * Calculate the size of the drawing area for the actual plot
1029          */
1030         draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
1031         draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
1032
1033
1034         /*
1035          * Draw the y axis and labels
1036          * (we always draw the y scale with 11 ticks along the axis)
1037          */
1038 #if GTK_CHECK_VERSION(2,22,0)
1039         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1040 #else
1041         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1042 #endif
1043         cairo_set_line_width (cr, 1.0);
1044         cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
1045         cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+0.5);
1046         cairo_stroke(cr);
1047         cairo_destroy(cr);
1048
1049         for(i=0;i<=10;i++){
1050                 int xwidth;
1051
1052                 xwidth=5;
1053                 if(!(i%5)){
1054                         /* first, middle and last tick are slightly longer */
1055                         xwidth=10;
1056                 }
1057                 /* draw the tick */
1058 #if GTK_CHECK_VERSION(2,22,0)
1059                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1060 #else
1061                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1062 #endif
1063                 cairo_set_line_width (cr, 1.0);
1064                 cairo_move_to(cr, 
1065                         user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, 
1066                         user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1067                 
1068                 cairo_line_to(cr, 
1069                         user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
1070                         user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1071                 cairo_stroke(cr);
1072                 cairo_destroy(cr);
1073                 /* draw the labels */
1074                 if(i==0){
1075                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1076                         pango_layout_set_text(layout, label_string, -1);
1077                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1078 #if GTK_CHECK_VERSION(2,22,0)
1079                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1080 #else
1081                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1082 #endif
1083                         cairo_move_to (cr, 
1084                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth, 
1085                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1086                         pango_cairo_show_layout (cr, layout);
1087                         cairo_destroy (cr);
1088                         cr = NULL;
1089                 }
1090                 if(i==5){
1091                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1092                         pango_layout_set_text(layout, label_string, -1);
1093                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1094 #if GTK_CHECK_VERSION(2,22,0)
1095                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1096 #else
1097                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1098 #endif
1099                         cairo_move_to (cr, 
1100                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth, 
1101                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1102                         pango_cairo_show_layout (cr, layout);
1103                         cairo_destroy (cr);
1104                         cr = NULL;
1105                 }
1106                 if(i==10){
1107                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1108                         pango_layout_set_text(layout, label_string, -1);
1109                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1110 #if GTK_CHECK_VERSION(2,22,0)
1111                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1112 #else
1113                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1114 #endif
1115                         cairo_move_to (cr, 
1116                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth, 
1117                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1118                         pango_cairo_show_layout (cr, layout);
1119                         cairo_destroy (cr);
1120                         cr = NULL;
1121                 }
1122         }
1123
1124
1125
1126         /*
1127          * if we have not specified the last_interval via the gui,
1128          * then just pick the current end of the capture so that is scrolls
1129          * nicely when doing live captures
1130          */
1131         if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1132                 last_interval=user_data->dlg.dialog_graph.max_interval;
1133         } else {
1134                 last_interval=user_data->dlg.dialog_graph.last_interval;
1135         }
1136
1137
1138
1139
1140 /*XXX*/
1141         /* plot the x-scale */
1142 #if GTK_CHECK_VERSION(2,22,0)
1143         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1144 #else
1145         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1146 #endif
1147         cairo_set_line_width (cr, 1.0);
1148         cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1149         cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1150         cairo_stroke(cr);
1151         cairo_destroy(cr);
1152
1153         if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1154                 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1155                 first_interval*=user_data->dlg.dialog_graph.interval;
1156         } else {
1157                 first_interval=0;
1158         }
1159
1160         interval_delta=1;
1161         delta_multiplier=5;
1162         while(interval_delta<((last_interval-first_interval)/10)){
1163                 interval_delta*=delta_multiplier;
1164                 if(delta_multiplier==5){
1165                         delta_multiplier=2;
1166                 } else {
1167                         delta_multiplier=5;
1168                 }
1169         }
1170
1171         for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1172                 int x, xlen;
1173
1174                 /* if pixels_per_tick is <5, only draw every 10 ticks */
1175                 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1176                         continue;
1177                 }
1178
1179                 if(current_interval%interval_delta){
1180                         xlen=5;
1181                 } else {
1182                         xlen=17;
1183                 }
1184
1185                 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1186 #if GTK_CHECK_VERSION(2,22,0)
1187                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1188 #else
1189                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1190 #endif
1191                 cairo_set_line_width (cr, 1.0);
1192                 cairo_move_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1193                 cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
1194                 cairo_stroke(cr);
1195                 cairo_destroy(cr);
1196
1197                 if(xlen==17){
1198                         if(user_data->dlg.dialog_graph.interval>=1000){
1199                                 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1200                         } else if(user_data->dlg.dialog_graph.interval>=100){
1201                                 g_snprintf(label_string, sizeof(label_string),
1202                                            "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1203                         } else if(user_data->dlg.dialog_graph.interval>=10){
1204                                 g_snprintf(label_string, sizeof(label_string),
1205                                            "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1206                         } else {
1207                                 g_snprintf(label_string, sizeof(label_string),
1208                                            "%d.%3ds", current_interval/1000,current_interval%1000);
1209                         }
1210                         pango_layout_set_text(layout, label_string, -1);
1211                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1212 #if GTK_CHECK_VERSION(2,22,0)
1213                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1214 #else
1215                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1216 #endif
1217                         cairo_move_to (cr, 
1218                                 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2, 
1219                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
1220                         pango_cairo_show_layout (cr, layout);
1221                         cairo_destroy (cr);
1222                         cr = NULL;
1223                 }
1224
1225         }
1226
1227
1228
1229
1230
1231
1232         /*
1233          * Draw "x" for Sequence Errors and "m" for Marks
1234          */
1235         /* Draw the labels Fwd and Rev */
1236         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
1237         pango_layout_set_text(layout, label_string, -1);
1238         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1239 #if GTK_CHECK_VERSION(2,22,0)
1240         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1241 #else
1242         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1243 #endif
1244         cairo_move_to (cr, 
1245                 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth, 
1246                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
1247         pango_cairo_show_layout (cr, layout);
1248         cairo_destroy (cr);
1249         cr = NULL;
1250
1251         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
1252         pango_layout_set_text(layout, label_string, -1);
1253         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1254 #if GTK_CHECK_VERSION(2,22,0)
1255         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1256 #else
1257         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1258 #endif
1259         cairo_move_to (cr, 
1260                 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth, 
1261                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
1262         pango_cairo_show_layout (cr, layout);
1263         cairo_destroy (cr);
1264         cr = NULL;
1265
1266         /* Draw the marks */
1267         for(i=MAX_GRAPHS-1;i>=0;i--){
1268                 guint32 interval;
1269                 guint32 x_pos/*, prev_x_pos*/;
1270
1271                 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1272                 if (!user_data->dlg.dialog_graph.graph[i].display){
1273                         continue;
1274                 }
1275                 /* initialize prev x/y to the low left corner of the graph */
1276                 /* prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border; */
1277
1278                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1279                         x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1280
1281                         if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1282                                 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1283                                         g_strlcpy(label_string, "x", sizeof(label_string));
1284                                 } else {
1285                                         g_strlcpy(label_string, "m", sizeof(label_string));
1286                                 }
1287
1288                                 pango_layout_set_text(layout, label_string, -1);
1289                                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1290 #if GTK_CHECK_VERSION(2,22,0)
1291                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1292 #else
1293                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1294 #endif
1295                                 cairo_move_to (cr, 
1296                                         x_pos-1-lwidth/2, 
1297                                         user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
1298                                 pango_cairo_show_layout (cr, layout);
1299                                 cairo_destroy (cr);
1300                                 cr = NULL;
1301                         }
1302
1303                         /* prev_x_pos=x_pos; */
1304                 }
1305         }
1306
1307         g_object_unref(G_OBJECT(layout));
1308
1309         /*
1310          * Loop over all graphs and draw them
1311          */
1312         for(i=MAX_GRAPHS-1;i>=0;i--){
1313                 guint32 interval;
1314                 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1315                 if (!user_data->dlg.dialog_graph.graph[i].display){
1316                         continue;
1317                 }
1318                 /* initialize prev x/y to the low left corner of the graph */
1319                 /* prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border; */
1320                 prev_y_pos=draw_height-1+top_y_border;
1321
1322                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1323                         guint32 val;
1324                         x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1325                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1326                         if(val>max_y){
1327                                 y_pos=0;
1328                         } else {
1329                                 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1330                         }
1331
1332                         /* dont need to draw anything if the segment
1333                          * is entirely above the top of the graph
1334                          */
1335                         if( (prev_y_pos==0) && (y_pos==0) ){
1336                                 prev_y_pos=y_pos;
1337                                 /* prev_x_pos=x_pos; */
1338                                 continue;
1339                         }
1340
1341                         if(val){
1342 #if GTK_CHECK_VERSION(2,22,0)
1343                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1344 #else
1345                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1346 #endif
1347                                 gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
1348                                 cairo_set_line_width (cr, 1.0);
1349                                 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1350                                 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1351                                 cairo_stroke(cr);
1352                                 cairo_destroy(cr);
1353                         }
1354
1355                         prev_y_pos=y_pos;
1356                         /* prev_x_pos=x_pos; */
1357                 }
1358         }
1359
1360
1361         cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1362
1363 #if GTK_CHECK_VERSION(2,22,0)
1364         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0); 
1365 #else
1366         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1367 #endif
1368         cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1369         cairo_fill (cr);
1370
1371         cairo_destroy (cr);
1372
1373
1374         /* update the scrollbar */
1375         gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
1376         gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1377         gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1378         if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1379                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1380         } else {
1381                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1382         }
1383         gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment, last_interval - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1384         gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1385         gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1386
1387 }
1388
1389 /****************************************************************************/
1390 static void dialog_graph_redraw(user_data_t* user_data)
1391 {
1392         user_data->dlg.dialog_graph.needs_redraw=TRUE;
1393         dialog_graph_draw(user_data);
1394 }
1395
1396 /****************************************************************************/
1397
1398 static void draw_area_destroy_cb(GtkWidget *widget _U_, gpointer data)
1399 {
1400         user_data_t *user_data = data;
1401
1402         user_data->dlg.dialog_graph.window = NULL;
1403 }
1404
1405 /****************************************************************************/
1406 static gboolean draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1407 {
1408         user_data_t *user_data = data;
1409         cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1410
1411 #if GTK_CHECK_VERSION(2,22,0)
1412         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0); 
1413 #else
1414         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1415 #endif
1416         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1417         cairo_fill (cr);
1418
1419         cairo_destroy (cr);
1420
1421         return FALSE;
1422 }
1423
1424 /****************************************************************************/
1425 static gboolean draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1426 {
1427         user_data_t *user_data = data;
1428         GtkAllocation widget_alloc;
1429         cairo_t *cr;
1430
1431 #if GTK_CHECK_VERSION(2,22,0)
1432         if(user_data->dlg.dialog_graph.surface){
1433                 g_object_unref(user_data->dlg.dialog_graph.surface);
1434                 user_data->dlg.dialog_graph.surface=NULL;
1435         }
1436         gtk_widget_get_allocation(widget, &widget_alloc);
1437         user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1438                         CAIRO_CONTENT_COLOR,
1439                         widget_alloc.width,
1440                         widget_alloc.height);
1441 #else
1442         if(user_data->dlg.dialog_graph.pixmap){
1443                 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1444                 user_data->dlg.dialog_graph.pixmap=NULL;
1445         }
1446
1447         gtk_widget_get_allocation(widget, &widget_alloc);
1448         user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1449                         widget_alloc.width,
1450                         widget_alloc.height,
1451                         -1);
1452 #endif
1453         user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
1454         user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
1455
1456 #if GTK_CHECK_VERSION(2,22,0)
1457         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1458 #else
1459         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1460 #endif
1461         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1462         cairo_set_source_rgb (cr, 1, 1, 1);
1463         cairo_fill (cr);
1464         cairo_destroy (cr);
1465
1466         dialog_graph_redraw(user_data);
1467         return TRUE;
1468 }
1469
1470 /****************************************************************************/
1471 static void scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1472 {
1473         user_data_t *user_data = data;
1474         guint32 mi;
1475
1476         mi=(guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment) + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1477         if(user_data->dlg.dialog_graph.last_interval==mi){
1478                 return;
1479         }
1480         if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1481             &&  (mi==user_data->dlg.dialog_graph.max_interval) ){
1482                 return;
1483         }
1484
1485         user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1486
1487         dialog_graph_redraw(user_data);
1488         return;
1489 }
1490
1491 /****************************************************************************/
1492 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1493 {
1494         user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1495         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), user_data);
1496
1497         gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1498
1499         /* signals needed to handle backing pixmap */
1500         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(draw_area_expose_event), user_data);
1501         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(draw_area_configure_event), user_data);
1502
1503         gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1504         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1505
1506         /* create the associated scrollbar */
1507         user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1508         user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1509         gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1510         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1511         g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1512 }
1513
1514 /****************************************************************************/
1515 static void disable_graph(dialog_graph_graph_t *dgg)
1516 {
1517         if (dgg->display) {
1518                 dgg->display=FALSE;
1519                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1520                     FALSE);
1521         }
1522 }
1523
1524 /****************************************************************************/
1525 static void filter_box_display_button_cb(GtkWidget *widget _U_, gpointer data)
1526 {
1527         dialog_graph_graph_t *dgg = data;
1528
1529         /* this graph is not active, just update display and redraw */
1530         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1531                 disable_graph(dgg);
1532                 dialog_graph_redraw(dgg->ud);
1533                 return;
1534         }
1535
1536         enable_graph(dgg);
1537         cf_retap_packets(&cfile);
1538         dialog_graph_redraw(dgg->ud);
1539
1540         return;
1541 }
1542
1543 /****************************************************************************/
1544 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1545 {
1546         GtkWidget *hbox;
1547         GtkWidget *label;
1548         char str[256];
1549
1550         hbox=gtk_hbox_new(FALSE, 3);
1551         gtk_container_add(GTK_CONTAINER(box), hbox);
1552         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1553         gtk_widget_show(hbox);
1554
1555         g_snprintf(str, sizeof(str), "Graph %d", num);
1556         dgg->display_button=gtk_toggle_button_new_with_label(str);
1557         gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1558         gtk_widget_show(dgg->display_button);
1559         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1560         g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_box_display_button_cb), dgg);
1561
1562         label=gtk_label_new(dgg->title);
1563         gtk_widget_show(label);
1564         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1565
1566 #if GTK_CHECK_VERSION(3,0,0)
1567 #else
1568         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1569         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1570         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1571         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1572         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1573 #endif
1574         return;
1575 }
1576
1577 /****************************************************************************/
1578 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1579 {
1580         GtkWidget *frame;
1581         GtkWidget *vbox;
1582         int i;
1583         GtkWidget *label;
1584
1585         frame=gtk_frame_new("Graphs");
1586         gtk_container_add(GTK_CONTAINER(box), frame);
1587         gtk_widget_show(frame);
1588
1589         vbox=gtk_vbox_new(FALSE, 1);
1590         gtk_container_add(GTK_CONTAINER(frame), vbox);
1591         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1592         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1593         gtk_widget_show(vbox);
1594
1595         for(i=0;i<MAX_GRAPHS;i++){
1596                 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1597         }
1598
1599         label=gtk_label_new("Label:    x = Wrong Seq. number      m = Mark set");
1600         gtk_widget_show(label);
1601         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1602
1603         return;
1604 }
1605
1606 /****************************************************************************/
1607 static void yscale_select(GtkWidget *item, gpointer data)
1608 {
1609         user_data_t *user_data = data;
1610         int i;
1611
1612         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1613
1614         user_data->dlg.dialog_graph.max_y_units_index=i;
1615         user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1616         dialog_graph_redraw(user_data);
1617 }
1618
1619 /****************************************************************************/
1620 static void pixels_per_tick_select(GtkWidget *item, gpointer data)
1621 {
1622         user_data_t *user_data = data;
1623         int i;
1624
1625         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1626
1627         user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1628         user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1629         dialog_graph_redraw(user_data);
1630 }
1631
1632 /****************************************************************************/
1633 static void tick_interval_select(GtkWidget *item, gpointer data)
1634 {
1635         user_data_t *user_data = data;
1636         int i;
1637
1638         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1639
1640         user_data->dlg.dialog_graph.interval_index=i;
1641         user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1642         cf_retap_packets(&cfile);
1643         dialog_graph_redraw(user_data);
1644 }
1645
1646 /****************************************************************************/
1647 static GtkWidget *
1648 create_yscale_max_menu_items(user_data_t* user_data)
1649 {
1650         char str[15];
1651         GtkWidget *combo_box;
1652         int i;
1653
1654         combo_box = gtk_combo_box_text_new();
1655
1656         for(i=0;i<MAX_YSCALE;i++){
1657                 if(yscale_max[i]==AUTO_MAX_YSCALE){
1658                         g_strlcpy(str,"Auto",sizeof(str));
1659                 } else if (yscale_max[i] < 1000000) {
1660                         g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1661                 } else {
1662                         g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1663                 }
1664                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1665         }
1666         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1667         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), user_data);
1668
1669         return combo_box;
1670 }
1671
1672 /****************************************************************************/
1673 static GtkWidget *
1674 create_pixels_per_tick_menu_items(user_data_t *user_data)
1675 {
1676         char str[5];
1677         GtkWidget *combo_box;
1678         int i;
1679
1680         combo_box = gtk_combo_box_text_new();
1681
1682         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1683                 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1684                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1685         }
1686         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1687
1688         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), user_data);
1689
1690         return combo_box;
1691 }
1692
1693 /****************************************************************************/
1694 static GtkWidget *
1695 create_tick_interval_menu_items(user_data_t *user_data)
1696 {
1697         GtkWidget *combo_box;
1698         char str[15];
1699         int i;
1700
1701         combo_box = gtk_combo_box_text_new();
1702
1703         for(i=0;i<MAX_TICK_VALUES;i++){
1704                 if(tick_interval_values[i]>=1000){
1705                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1706                 } else if(tick_interval_values[i]>=100){
1707                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1708                 } else if(tick_interval_values[i]>=10){
1709                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1710                 } else {
1711                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1712                 }
1713                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1714         }
1715         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1716         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), user_data);
1717
1718         return combo_box;
1719 }
1720
1721 /****************************************************************************/
1722 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1723 {
1724         GtkWidget *hbox;
1725         GtkWidget *label;
1726         GtkWidget *combo_box;
1727
1728         hbox=gtk_hbox_new(FALSE, 0);
1729         gtk_container_add(GTK_CONTAINER(box), hbox);
1730         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1731         gtk_widget_show(hbox);
1732
1733         label=gtk_label_new(name);
1734         gtk_widget_show(label);
1735         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1736
1737         combo_box = (*func)(user_data);
1738         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1739         gtk_widget_show(combo_box);
1740 }
1741
1742 /****************************************************************************/
1743 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1744 {
1745         GtkWidget *frame_vbox;
1746         GtkWidget *frame;
1747         GtkWidget *vbox;
1748
1749         frame_vbox=gtk_vbox_new(FALSE, 0);
1750         gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1751         gtk_widget_show(frame_vbox);
1752
1753         frame = gtk_frame_new("X Axis");
1754         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1755         gtk_widget_show(frame);
1756
1757         vbox=gtk_vbox_new(FALSE, 0);
1758         gtk_container_add(GTK_CONTAINER(frame), vbox);
1759         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1760         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1761         gtk_widget_show(vbox);
1762
1763         create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1764         create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1765
1766         frame = gtk_frame_new("Y Axis");
1767         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1768         gtk_widget_show(frame);
1769
1770         vbox=gtk_vbox_new(FALSE, 0);
1771         gtk_container_add(GTK_CONTAINER(frame), vbox);
1772         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1773         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1774         gtk_widget_show(vbox);
1775
1776         create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1777
1778         return;
1779 }
1780
1781 /****************************************************************************/
1782 static void dialog_graph_init_window(user_data_t* user_data)
1783 {
1784         GtkWidget *vbox;
1785         GtkWidget *hbox;
1786         GtkWidget *bt_close;
1787
1788         /* create the main window */
1789         user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs");   /* transient_for top_level */
1790
1791         vbox=gtk_vbox_new(FALSE, 0);
1792         gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1793         gtk_widget_show(vbox);
1794
1795         create_draw_area(user_data, vbox);
1796
1797         hbox=gtk_hbox_new(FALSE, 3);
1798         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1799         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1800         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1801         gtk_widget_show(hbox);
1802
1803         create_filter_area(user_data, hbox);
1804         create_ctrl_area(user_data, hbox);
1805
1806         dialog_graph_set_title(user_data);
1807
1808         hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1809         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1810         gtk_widget_show(hbox);
1811
1812         bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1813         window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1814
1815         g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1816
1817         gtk_widget_show(user_data->dlg.dialog_graph.window);
1818         window_present(user_data->dlg.dialog_graph.window);
1819
1820 }
1821
1822
1823 /****************************************************************************/
1824 static void on_graph_bt_clicked(GtkWidget *bt _U_, gpointer data)
1825 {
1826         user_data_t *user_data = data;
1827
1828         if (user_data->dlg.dialog_graph.window != NULL) {
1829                 /* There's already a graph window; reactivate it. */
1830                 reactivate_window(user_data->dlg.dialog_graph.window);
1831                 return;
1832         }
1833
1834         dialog_graph_init_window(user_data);
1835
1836 }
1837
1838 /****************************************************************************/
1839 static void on_goto_bt_clicked(GtkWidget *bt _U_, gpointer data)
1840 {
1841         user_data_t *user_data = data;
1842         GtkTreeIter iter;
1843         GtkTreeModel *model;
1844         GtkTreeSelection *selection;
1845         guint fnumber;
1846
1847         selection = user_data->dlg.selected_list_sel;
1848
1849         if (selection==NULL)
1850                 return;
1851
1852         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1853                 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1854                 cf_goto_frame(&cfile, fnumber);
1855         }
1856 }
1857
1858
1859 static void draw_stat(user_data_t *user_data);
1860
1861 /****************************************************************************/
1862 /* re-dissects all packets */
1863 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data)
1864 {
1865         user_data_t *user_data = data;
1866         GString *error_string;
1867
1868         /* remove tap listener */
1869         protect_thread_critical_region();
1870         remove_tap_listener(user_data);
1871         unprotect_thread_critical_region();
1872
1873         /* register tap listener */
1874         error_string = register_tap_listener("IAX2", user_data, NULL, 0,
1875                 iax2_reset, iax2_packet, iax2_draw);
1876         if (error_string != NULL) {
1877                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1878                         g_string_free(error_string, TRUE);
1879                 return;
1880         }
1881
1882         /* retap all packets */
1883         cf_retap_packets(&cfile);
1884
1885         /* draw statistics info */
1886         draw_stat(user_data);
1887
1888 }
1889
1890 /****************************************************************************/
1891 static void on_next_bt_clicked(GtkWidget *bt _U_, gpointer data)
1892 {
1893         user_data_t *user_data = data;
1894         GtkTreeIter iter;
1895         GtkTreeModel *model;
1896         gchar *text;
1897         GtkTreeSelection *selection;
1898         GtkTreePath *path;
1899
1900         selection = user_data->dlg.selected_list_sel;
1901
1902         if (selection==NULL)
1903                 return;
1904
1905 try_again:
1906         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1907                 while (gtk_tree_model_iter_next (model,&iter)) {
1908                         gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1909                         if (strcmp(text, OK_TEXT) != 0) {
1910                                 gtk_tree_selection_select_iter (selection, &iter);
1911                                 path = gtk_tree_model_get_path(model, &iter);
1912                                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1913                                                 path,
1914                                                 NULL, FALSE, 0, 0);
1915                                 gtk_tree_path_free(path);
1916                                 g_free (text);
1917                                 return;
1918                         }
1919                         g_free (text);
1920                 }
1921                 /* wrap around */
1922                 if (user_data->dlg.number_of_nok>1){
1923                         /* Get the first iter and select it before starting over */
1924                         gtk_tree_model_get_iter_first(model, &iter);
1925                         gtk_tree_selection_select_iter (selection, &iter);
1926                         goto try_again;
1927                 }
1928         }
1929 }
1930
1931 /****************************************************************************/
1932 /* when we want to save the information */
1933 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1934 {
1935         gchar        *g_dest;
1936         GtkWidget    *rev, *forw, *both;
1937         user_data_t  *user_data;
1938
1939         GtkListStore *store;
1940         GtkTreeIter   iter;
1941         GtkTreeModel *model;
1942         gboolean      more_items = TRUE;
1943
1944         /* To Hold data from the list row */
1945         guint   packet;         /* Packet                       */
1946         gfloat  delta;          /* Delta(ms)                    */
1947         gfloat  jitter;         /* Jitter(ms)                   */
1948         gfloat  ipbw;           /* IP BW(kbps)                  */
1949         char   *status_str;     /* Status                       */
1950         char   *date_str;       /* Date                         */
1951         guint   length;         /* Length                       */
1952
1953         FILE *fp;
1954         int j;
1955
1956         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1957
1958         /* Perhaps the user specified a directory instead of a file.
1959          * Check whether they did.
1960          */
1961         if (test_for_directory(g_dest) == EISDIR) {
1962                 /* It's a directory - set the file selection box to display it. */
1963                 set_last_open_dir(g_dest);
1964                 file_selection_set_current_folder(fc, get_last_open_dir());
1965                 gtk_file_chooser_set_current_name(fc, "");
1966                 g_free(g_dest);
1967                 return FALSE; /* run the dialog again */
1968         }
1969         rev  = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1970         forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1971         both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1972         user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1973
1974         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
1975                 fp = ws_fopen(g_dest, "w");
1976                 if (fp == NULL) {
1977                         open_failure_alert_box(g_dest, errno, TRUE);
1978                         g_free(g_dest);
1979                         return TRUE; /* we're done */
1980                 }
1981
1982                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
1983                         fprintf(fp, "Forward\n");
1984                         if (ferror(fp)) {
1985                                 write_failure_alert_box(g_dest, errno);
1986                                 fclose(fp);
1987                                 g_free(g_dest);
1988                                 return TRUE; /* we're done */
1989                         }
1990                 }
1991
1992                 for(j = 0; j < NUM_COLS; j++) {
1993                         if (j == 0) {
1994                                 fprintf(fp,"\"%s\"",titles[j]);
1995                         } else {
1996                                 fprintf(fp,",\"%s\"",titles[j]);
1997                         }
1998                 }
1999                 fprintf(fp,"\n");
2000                 if (ferror(fp)) {
2001                         write_failure_alert_box(g_dest, errno);
2002                         fclose(fp);
2003                         g_free(g_dest);
2004                         return TRUE; /* we're done */
2005                 }
2006                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2007                 store = GTK_LIST_STORE(model);
2008                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2009
2010                         while (more_items) {
2011                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2012                                                    0, &packet,
2013                                                    1, &delta,
2014                                                    2, &jitter,
2015                                                    3, &ipbw,
2016                                                    4, &status_str,
2017                                                    5, &date_str,
2018                                                    6, &length,
2019                                                    -1);
2020                                 fprintf(fp, "\"%u\"",packet);
2021                                 fprintf(fp, ",\"%.2f\"", delta);
2022                                 fprintf(fp, ",\"%.2f\"", jitter);
2023                                 fprintf(fp, ",\"%.2f\"", ipbw);
2024                                 fprintf(fp, ",\"%s\"", status_str);
2025                                 fprintf(fp, ",\"%s\"", date_str);
2026                                 fprintf(fp, ",\"%u\"", length);
2027                                 fprintf(fp,"\n");
2028                                 g_free(status_str);
2029                                 g_free(date_str);
2030                                 if (ferror(fp)) {
2031                                         write_failure_alert_box(g_dest, errno);
2032                                         fclose(fp);
2033                                         g_free(g_dest);
2034                                         return TRUE; /* we're done */
2035                                 }
2036
2037                                 more_items = gtk_tree_model_iter_next (model,&iter);
2038                         }
2039                 }
2040
2041                 if (fclose(fp) == EOF) {
2042                         write_failure_alert_box(g_dest, errno);
2043                         g_free(g_dest);
2044                         return TRUE; /* we're done */
2045                 }
2046         }
2047
2048         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2049
2050                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2051                         fp = ws_fopen(g_dest, "a");
2052                         if (fp == NULL) {
2053                                 open_failure_alert_box(g_dest, errno, TRUE);
2054                                 g_free(g_dest);
2055                                 return TRUE; /* we're done */
2056                         }
2057                         fprintf(fp, "\nReverse\n");
2058                         if (ferror(fp)) {
2059                                 write_failure_alert_box(g_dest, errno);
2060                                 fclose(fp);
2061                                 g_free(g_dest);
2062                                 return TRUE; /* we're done */
2063                         }
2064                 } else {
2065                         fp = ws_fopen(g_dest, "w");
2066                         if (fp == NULL) {
2067                                 open_failure_alert_box(g_dest, errno, TRUE);
2068                                 g_free(g_dest);
2069                                 return TRUE; /* we're done */
2070                         }
2071                 }
2072                 for(j = 0; j < NUM_COLS; j++) {
2073                         if (j == 0) {
2074                                 fprintf(fp,"\"%s\"",titles[j]);
2075                         } else {
2076                                 fprintf(fp,",\"%s\"",titles[j]);
2077                         }
2078                 }
2079                 fprintf(fp,"\n");
2080                 if (ferror(fp)) {
2081                         write_failure_alert_box(g_dest, errno);
2082                         fclose(fp);
2083                         g_free(g_dest);
2084                         return TRUE; /* we're done */
2085                 }
2086                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2087                 store = GTK_LIST_STORE(model);
2088                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2089
2090                         more_items = TRUE;
2091
2092                         while (more_items) {
2093                                 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2094                                                    0, &packet,
2095                                                    1, &delta,
2096                                                    2, &jitter,
2097                                                    3, &ipbw,
2098                                                    4, &status_str,
2099                                                    5, &date_str,
2100                                                    6, &length,
2101                                                    -1);
2102                                 fprintf(fp, "\"%u\"",packet);
2103                                 fprintf(fp, ",\"%.2f\"", delta);
2104                                 fprintf(fp, ",\"%.2f\"", jitter);
2105                                 fprintf(fp, ",\"%.2f\"", ipbw);
2106                                 fprintf(fp, ",\"%s\"", status_str);
2107                                 fprintf(fp, ",\"%s\"", date_str);
2108                                 fprintf(fp, ",\"%u\"", length);
2109                                 fprintf(fp,"\n");
2110                                 g_free(status_str);
2111                                 g_free(date_str);
2112                                 if (ferror(fp)) {
2113                                         write_failure_alert_box(g_dest, errno);
2114                                         fclose(fp);
2115                                         g_free(g_dest);
2116                                         return TRUE; /* we're done */
2117                                 }
2118
2119                                 more_items = gtk_tree_model_iter_next (model,&iter);
2120                         }
2121                 }
2122                 if (fclose(fp) == EOF) {
2123                         write_failure_alert_box(g_dest, errno);
2124                         g_free(g_dest);
2125                         return TRUE; /* we're done */
2126                 }
2127         }
2128         g_free(g_dest);
2129         return TRUE; /* we're done */
2130 }
2131
2132 static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2133 {
2134         user_data_t *user_data = data;
2135
2136         user_data->dlg.save_csv_as_w = NULL;
2137 }
2138
2139 /* when the user wants to save the csv information in a file */
2140 static void save_csv_as_cb(GtkWidget *bt _U_, gpointer data)
2141 {
2142         user_data_t *user_data = data;
2143         GtkWidget *vertb;
2144         GtkWidget *table1;
2145         GtkWidget *label_format;
2146         GtkWidget *channels_label;
2147         GtkWidget *forward_rb;
2148         GtkWidget *reversed_rb;
2149         GtkWidget *both_rb;
2150
2151 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2152         if (user_data->dlg.save_csv_as_w != NULL) {
2153                 /* There's already a Save CSV info dialog box; reactivate it. */
2154                 reactivate_window(user_data->dlg.save_csv_as_w);
2155                 return;
2156         }
2157 #endif
2158         user_data->dlg.save_csv_as_w =
2159                 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2160                                             GTK_WINDOW(user_data->dlg.notebook),
2161                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2162                                             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2163                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2164                                             NULL);
2165         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2166         gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2167
2168         /* Build our "extra widget" to be added to the file chooser widget */
2169         /* Container for each row of widgets */
2170         vertb = gtk_vbox_new(FALSE, 0);
2171         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2172         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2173         gtk_widget_show (vertb);
2174
2175         table1 = gtk_table_new (2, 4, FALSE);
2176         gtk_widget_show (table1);
2177         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2178         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2179         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2180
2181         label_format = gtk_label_new ("Format:        Comma Separated Values");
2182         gtk_widget_show (label_format);
2183         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2184                           (GtkAttachOptions) (GTK_FILL),
2185                           (GtkAttachOptions) (0), 0, 0);
2186         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2187
2188         channels_label = gtk_label_new ("Channels:    ");
2189         gtk_widget_show (channels_label);
2190         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2191                           (GtkAttachOptions) (GTK_FILL),
2192                           (GtkAttachOptions) (0), 0, 0);
2193         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2194
2195         forward_rb = gtk_radio_button_new_with_label (NULL, "forward    ");
2196         gtk_widget_show (forward_rb);
2197         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2198                           (GtkAttachOptions) (GTK_FILL),
2199                           (GtkAttachOptions) (0), 0, 0);
2200
2201         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
2202         gtk_widget_show (reversed_rb);
2203         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2204                           (GtkAttachOptions) (GTK_FILL),
2205                           (GtkAttachOptions) (0), 0, 0);
2206
2207         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2208         gtk_widget_show (both_rb);
2209         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2210                           (GtkAttachOptions) (GTK_FILL),
2211                           (GtkAttachOptions) (0), 0, 0);
2212
2213         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2214
2215         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb",  forward_rb);
2216         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2217         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb",     both_rb);
2218         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data",   user_data);
2219
2220         g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2221                          G_CALLBACK(window_delete_event_cb), NULL);
2222         g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2223                          G_CALLBACK(save_csv_as_destroy_cb), user_data);
2224
2225         gtk_widget_show(user_data->dlg.save_csv_as_w);
2226         window_present(user_data->dlg.save_csv_as_w);
2227
2228         /* "Run" the GtkFileChooserDialog.                                              */
2229         /* Upon exit: If "Accept" run the OK callback.                                  */
2230         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
2231         /*            Destroy the window.                                               */
2232         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
2233         /*      return with a TRUE status so that the dialog window will be destroyed.  */
2234         /*      Trying to re-run the dialog after popping up an alert box will not work */
2235         /*       since the user will not be able to dismiss the alert box.              */
2236         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
2237         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
2238         /*                                                                              */
2239         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
2240         /*            GtkFileChooserDialog.                                             */
2241         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2242                 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2243                         break; /* we're done */
2244                 }
2245         }
2246         window_destroy(user_data->dlg.save_csv_as_w);
2247 }
2248
2249 /****************************************************************************/
2250 static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2251 {
2252         user_data_t *user_data = data;
2253
2254         /* Note that we no longer have a Save voice info dialog box. */
2255         user_data->dlg.save_voice_as_w = NULL;
2256 }
2257
2258 /****************************************************************************/
2259 /* here we save it into a file that user specified */
2260 /* XXX what about endians here? could go something wrong? */
2261 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2262 {
2263         int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
2264         gchar f_pd[1] = {0};
2265         gchar r_pd[1] = {0};
2266         gint16 sample;
2267         gchar pd[4];
2268         guint32 f_write_silence = 0;
2269         guint32 r_write_silence = 0;
2270         progdlg_t *progbar;
2271         guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2272         gboolean stop_flag = FALSE;
2273         /*size_t nchars;*/
2274
2275         forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2276         if (forw_fd < 0)
2277                 return FALSE;
2278         rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2279         if (rev_fd < 0) {
2280                 ws_close(forw_fd);
2281                 return FALSE;
2282         }
2283
2284         /* open file for saving */
2285         to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2286         if (to_fd < 0) {
2287                 ws_close(forw_fd);
2288                 ws_close(rev_fd);
2289                 return FALSE;
2290         }
2291
2292         progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2293
2294         if      (format == SAVE_AU_FORMAT) /* au format */
2295         {
2296                 /* First we write the .au header. XXX Hope this is endian independant */
2297                 /* the magic word 0x2e736e64 == .snd */
2298                 /* XXX: Should we be checking for write errors below ? */
2299                 phtonl(pd, 0x2e736e64);
2300                 fwritten = ws_write(to_fd, pd, 4);
2301                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2302                         ws_close(forw_fd);
2303                         ws_close(rev_fd);
2304                         ws_close(to_fd);
2305                         destroy_progress_dlg(progbar);
2306                         return FALSE;
2307                 }
2308                 /* header offset == 24 bytes */
2309                 phtonl(pd, 24);
2310                 fwritten = ws_write(to_fd, pd, 4);
2311                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2312                         ws_close(forw_fd);
2313                         ws_close(rev_fd);
2314                         ws_close(to_fd);
2315                         destroy_progress_dlg(progbar);
2316                         return FALSE;
2317                 }
2318                 /* total length, it is permited to set this to 0xffffffff */
2319                 phtonl(pd, -1);
2320                 fwritten = ws_write(to_fd, pd, 4);
2321                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2322                         ws_close(forw_fd);
2323                         ws_close(rev_fd);
2324                         ws_close(to_fd);
2325                         destroy_progress_dlg(progbar);
2326                         return FALSE;
2327                 }
2328                 /* encoding format == 16-bit linear PCM */
2329                 phtonl(pd, 3);
2330                 fwritten = ws_write(to_fd, pd, 4);
2331                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2332                         ws_close(forw_fd);
2333                         ws_close(rev_fd);
2334                         ws_close(to_fd);
2335                         destroy_progress_dlg(progbar);
2336                         return FALSE;
2337                 }
2338                 /* sample rate == 8000 Hz */
2339                 phtonl(pd, 8000);
2340                 fwritten = ws_write(to_fd, pd, 4);
2341                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2342                         ws_close(forw_fd);
2343                         ws_close(rev_fd);
2344                         ws_close(to_fd);
2345                         destroy_progress_dlg(progbar);
2346                         return FALSE;
2347                 }
2348                 /* channels == 1 */
2349                 phtonl(pd, 1);
2350                 fwritten = ws_write(to_fd, pd, 4);
2351                 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2352                         ws_close(forw_fd);
2353                         ws_close(rev_fd);
2354                         ws_close(to_fd);
2355                         destroy_progress_dlg(progbar);
2356                         return FALSE;
2357                 }
2358
2359                 switch (channels) {
2360                         /* only forward direction */
2361                         case SAVE_FORWARD_DIRECTION_MASK: {
2362                                 progbar_count = user_data->forward.saveinfo.count;
2363                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2364                                 while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
2365                                         if(stop_flag)
2366                                                 break;
2367                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2368                                                 update_progress_dlg(progbar,
2369                                                         (gfloat) count/progbar_count, "Saving");
2370                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2371                                         }
2372                                         count++;
2373
2374                                         if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2375                                                 sample = ulaw2linear(*f_pd);
2376                                                 phtons(pd, sample);
2377                                         }
2378                                         else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2379                                                 sample = alaw2linear(*f_pd);
2380                                                 phtons(pd, sample);
2381                                         }
2382                                         else{
2383                                                 ws_close(forw_fd);
2384                                                 ws_close(rev_fd);
2385                                                 ws_close(to_fd);
2386                                                 destroy_progress_dlg(progbar);
2387                                                 return FALSE;
2388                                         }
2389
2390                                         fwritten = ws_write(to_fd, pd, 2);
2391                                         if ((fwritten < 2) || (fwritten < 0) || (fread_cnt < 0)) {
2392                                                 ws_close(forw_fd);
2393                                                 ws_close(rev_fd);
2394                                                 ws_close(to_fd);
2395                                                 destroy_progress_dlg(progbar);
2396                                                 return FALSE;
2397                                         }
2398                                 }
2399                                 break;
2400                         }
2401                         /* only reversed direction */
2402                         case SAVE_REVERSE_DIRECTION_MASK: {
2403                                 progbar_count = user_data->reversed.saveinfo.count;
2404                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2405                                 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2406                                         if(stop_flag)
2407                                                 break;
2408                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2409                                                 update_progress_dlg(progbar,
2410                                                         (gfloat) count/progbar_count, "Saving");
2411                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2412                                         }
2413                                         count++;
2414
2415                                         if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2416                                                 sample = ulaw2linear(*r_pd);
2417                                                 phtons(pd, sample);
2418                                         }
2419                                         else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2420                                                 sample = alaw2linear(*r_pd);
2421                                                 phtons(pd, sample);
2422                                         }
2423                                         else{
2424                                                 ws_close(forw_fd);
2425                                                 ws_close(rev_fd);
2426                                                 ws_close(to_fd);
2427                                                 destroy_progress_dlg(progbar);
2428                                                 return FALSE;
2429                                         }
2430
2431                                         rwritten = ws_write(to_fd, pd, 2);
2432                                         if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2433                                                 ws_close(forw_fd);
2434                                                 ws_close(rev_fd);
2435                                                 ws_close(to_fd);
2436                                                 destroy_progress_dlg(progbar);
2437                                                 return FALSE;
2438                                         }
2439                                 }
2440                                 break;
2441                         }
2442                         /* both directions */
2443                         case SAVE_BOTH_DIRECTION_MASK: {
2444                                 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2445                                                 (progbar_count = user_data->forward.saveinfo.count) :
2446                                                         (progbar_count = user_data->reversed.saveinfo.count);
2447                                 progbar_quantum = progbar_count/100;
2448                                 /* since conversation in one way can start later than in the other one,
2449                                  * we have to write some silence information for one channel */
2450                                 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2451                                         f_write_silence = (guint32)
2452                                                 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2453                                 }
2454                                 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2455                                         r_write_silence = (guint32)
2456                                                 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2457                                 }
2458                                 for(;;) {
2459                                         if(stop_flag)
2460                                                 break;
2461                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2462                                                 update_progress_dlg(progbar,
2463                                                         (gfloat) count/progbar_count, "Saving");
2464                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2465                                         }
2466                                         count++;
2467                                         if(f_write_silence > 0) {
2468                                                 rread = read(rev_fd, r_pd, 1);
2469                                                 switch (user_data->forward.statinfo.reg_pt) {
2470                                                 case AST_FORMAT_ULAW:
2471                                                         *f_pd = SILENCE_PCMU;
2472                                                         break;
2473                                                 case AST_FORMAT_ALAW:
2474                                                         *f_pd = SILENCE_PCMA;
2475                                                         break;
2476                                                 }
2477                                                 fread_cnt = 1;
2478                                                 f_write_silence--;
2479                                         }
2480                                         else if(r_write_silence > 0) {
2481                                                 fread_cnt = read(forw_fd, f_pd, 1);
2482                                                 switch (user_data->reversed.statinfo.reg_pt) {
2483                                                 case AST_FORMAT_ULAW:
2484                                                         *r_pd = SILENCE_PCMU;
2485                                                         break;
2486                                                 case AST_FORMAT_ALAW:
2487                                                         *r_pd = SILENCE_PCMA;
2488                                                         break;
2489                                                 }
2490                                                 rread = 1;
2491                                                 r_write_silence--;
2492                                         }
2493                                         else {
2494                                                 fread_cnt = read(forw_fd, f_pd, 1);
2495                                                 rread = read(rev_fd, r_pd, 1);
2496                                         }
2497                                         if ((rread == 0) && (fread_cnt == 0))
2498                                                 break;
2499                                         if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2500                                                 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2501                                                 phtons(pd, sample);
2502                                         }
2503                                         else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2504                                                 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2505                                                 phtons(pd, sample);
2506                                         }
2507                                         else
2508                                         {
2509                                                 ws_close(forw_fd);
2510                                                 ws_close(rev_fd);
2511                                                 ws_close(to_fd);
2512                                                 destroy_progress_dlg(progbar);
2513                                                 return FALSE;
2514                                         }
2515
2516
2517                                         rwritten = ws_write(to_fd, pd, 2);
2518                                         if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
2519                                                 ws_close(forw_fd);
2520                                                 ws_close(rev_fd);
2521                                                 ws_close(to_fd);
2522                                                 destroy_progress_dlg(progbar);
2523                                                 return FALSE;
2524                                         }
2525                                 }
2526                         }
2527                 }
2528         }
2529         else if (format == SAVE_RAW_FORMAT)     /* raw format */
2530         {
2531                 int fd;
2532                 switch (channels) {
2533                         /* only forward direction */
2534                         case SAVE_FORWARD_DIRECTION_MASK: {
2535                                 progbar_count = user_data->forward.saveinfo.count;
2536                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2537                                 fd = forw_fd;
2538                                 break;
2539                         }
2540                         /* only reversed direction */
2541                         case SAVE_REVERSE_DIRECTION_MASK: {
2542                                 progbar_count = user_data->reversed.saveinfo.count;
2543                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2544                                 fd = rev_fd;
2545                                 break;
2546                         }
2547                         default: {
2548                                 ws_close(forw_fd);
2549                                 ws_close(rev_fd);
2550                                 ws_close(to_fd);
2551                                 destroy_progress_dlg(progbar);
2552                                 return FALSE;
2553                         }
2554                 }
2555
2556
2557
2558                 /* XXX how do you just copy the file? */
2559                 while ((rread = read(fd, pd, 1)) > 0) {
2560                         if(stop_flag)
2561                                 break;
2562                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2563                                 update_progress_dlg(progbar,
2564                                         (gfloat) count/progbar_count, "Saving");
2565                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2566                         }
2567                         count++;
2568
2569                         rwritten = ws_write(to_fd, pd, 1);
2570
2571                         if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2572                                 ws_close(forw_fd);
2573                                 ws_close(rev_fd);
2574                                 ws_close(to_fd);
2575                                 destroy_progress_dlg(progbar);
2576                                 return FALSE;
2577                         }
2578                 }
2579         }
2580
2581         destroy_progress_dlg(progbar);
2582         ws_close(forw_fd);
2583         ws_close(rev_fd);
2584         ws_close(to_fd);
2585         return TRUE;
2586 }
2587
2588
2589 /****************************************************************************/
2590 /* the user wants to save in a file */
2591 /* XXX support for different formats is currently commented out */
2592 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2593 {
2594         gchar       *g_dest;
2595         /*GtkWidget   *wav, *sw;*/
2596         GtkWidget   *au, *raw;
2597         GtkWidget   *rev, *forw, *both;
2598         user_data_t *user_data;
2599         gint         channels , format;
2600
2601         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2602
2603         /* Perhaps the user specified a directory instead of a file.
2604            Check whether they did. */
2605         if (test_for_directory(g_dest) == EISDIR) {
2606                 /* It's a directory - set the file selection box to display it. */
2607                 set_last_open_dir(g_dest);
2608                 file_selection_set_current_folder(fc, get_last_open_dir());
2609                 gtk_file_chooser_set_current_name(fc, "");
2610                 g_free(g_dest);
2611                 return FALSE; /* run the dialog again */
2612         }
2613
2614 #if 0
2615         wav  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2616         sw   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2617 #endif
2618         au   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2619         raw  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2620         rev  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2621         forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2622         both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2623         user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2624
2625         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2626         * we don't support that codec. So we pop up a warning. Maybe it would be better to
2627         * disable the ok button or disable the buttons for direction if only one is not ok. The
2628         * problem is if we open the save voice dialog and then click the refresh button and maybe
2629         * the state changes, so we can't save anymore. In this case we should be able to update
2630         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2631         */
2632
2633         /* we can not save in both directions */
2634         if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2635                 /* there are many combinations here, we just exit when first matches */
2636                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2637                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2638                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2639                         "Can't save in a file: Unsupported codec!");
2640                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2641                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2642                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2643                         "Can't save in a file: Wrong length of captured packets!");
2644                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2645                         (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2646                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2647                         "Can't save in a file: Not all data in all packets was captured!");
2648                 else
2649                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2650                         "Can't save in a file: File I/O problem!");
2651                 g_free(g_dest);
2652                 return TRUE; /* we're done */
2653         }
2654         /* we can not save forward direction */
2655         else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
2656                 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2657                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2658                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2659                         "Can't save forward direction in a file: Unsupported codec!");
2660                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2661                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2662                         "Can't save forward direction in a file: Wrong length of captured packets!");
2663                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2664                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2665                         "Can't save forward direction in a file: Not all data in all packets was captured!");
2666                 else
2667                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2668                         "Can't save forward direction in a file: File I/O problem!");
2669                 g_free(g_dest);
2670                 return TRUE; /* we're done */
2671         }
2672         /* we can not save reversed direction */
2673         else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
2674                 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2675                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2676                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2677                         "Can't save reversed direction in a file: Unsupported codec!");
2678                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2679                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2680                         "Can't save reversed direction in a file: Wrong length of captured packets!");
2681                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2682                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2683                         "Can't save reversed direction in a file: Not all data in all packets was captured!");
2684                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2685                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2686                         "Can't save reversed direction in a file: No IAX2 data!");
2687                 else
2688                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2689                         "Can't save reversed direction in a file: File I/O problem!");
2690                 g_free(g_dest);
2691                 return TRUE; /* we're done */
2692         }
2693
2694 #if 0
2695         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2696                 format = SAVE_WAV_FORMAT;
2697         else
2698 #endif
2699         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2700                 format = SAVE_AU_FORMAT;
2701 #if 0
2702         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2703                 format = SAVE_SW_FORMAT;
2704 #endif
2705         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2706                 format = SAVE_RAW_FORMAT;
2707         else
2708                 format = SAVE_NONE_FORMAT;
2709
2710         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2711                 channels = SAVE_REVERSE_DIRECTION_MASK;
2712         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2713                 channels = SAVE_BOTH_DIRECTION_MASK;
2714         else
2715                 channels = SAVE_FORWARD_DIRECTION_MASK;
2716
2717         /* direction/format validity*/
2718         if (format == SAVE_AU_FORMAT)
2719         {
2720                 /* make sure streams are alaw/ulaw */
2721                 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2722                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2723                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2724                         g_free(g_dest);
2725                         return TRUE; /* we're done */
2726                 }
2727                 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2728                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2729                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2730                         g_free(g_dest);
2731                         return TRUE; /* we're done */
2732                 }
2733                 /* make sure pt's don't differ */
2734                 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2735                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2736                                 "Can't save in a file: Forward and reverse direction differ in type");
2737                         g_free(g_dest);
2738                         return TRUE; /* we're done */
2739                 }
2740         }
2741         else if (format == SAVE_RAW_FORMAT)
2742         {
2743                 /* can't save raw in both directions */
2744                 if (channels == SAVE_BOTH_DIRECTION_MASK){
2745                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2746                                 "Can't save in a file: Unable to save raw data in both directions");
2747                         g_free(g_dest);
2748                         return TRUE; /* we're done */
2749                 }
2750         }
2751         else
2752         {
2753                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2754                         "Can't save in a file: Invalid save format");
2755                 g_free(g_dest);
2756                 return TRUE; /* we're done */
2757         }
2758
2759         if(!copy_file(g_dest, channels, format, user_data)) {
2760                 /* XXX - report the error type! */
2761                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2762                         "An error occurred while saving voice in a file!");
2763                 g_free(g_dest);
2764                 return TRUE; /* we're done */
2765         }
2766         g_free(g_dest);
2767         return TRUE; /* we're done */
2768 }
2769
2770 /****************************************************************************/
2771 /* when the user wants to save the voice information in a file */
2772 /* XXX support for different formats is currently commented out */
2773 static void save_voice_as_cb(GtkWidget *bt _U_, gpointer data)
2774 {
2775         user_data_t *user_data = data;
2776         GtkWidget *vertb;
2777         GtkWidget *table1;
2778         GtkWidget *label_format;
2779         GtkWidget *channels_label;
2780         GtkWidget *forward_rb;
2781         GtkWidget *reversed_rb;
2782         GtkWidget *both_rb;
2783         /*GtkWidget *wav_rb;  GtkWidget *sw_rb;*/
2784         GtkWidget *au_rb;
2785         GtkWidget *raw_rb;
2786
2787         /* if we can't save in a file: wrong codec, cut packets or other errors */
2788         /* should the error arise here or later when you click ok button ?
2789         * if we do it here, then we must disable the refresh button, so we don't do it here */
2790
2791 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2792         if (user_data->dlg.save_voice_as_w != NULL) {
2793                 /* There's already a Save voice info dialog box; reactivate it. */
2794                 reactivate_window(user_data->dlg.save_voice_as_w);
2795                 return;
2796         }
2797 #endif
2798         /* XXX - use file_selection from dlg_utils instead! */
2799         user_data->dlg.save_voice_as_w =
2800                 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2801                                             GTK_WINDOW(user_data->dlg.notebook),
2802                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2803                                             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2804                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2805                                             NULL);
2806         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2807         gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2808
2809         /* Container for each row of widgets */
2810         vertb = gtk_vbox_new(FALSE, 0);
2811         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2812         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2813         gtk_widget_show (vertb);
2814
2815         table1 = gtk_table_new (2, 4, FALSE);
2816         gtk_widget_show (table1);
2817         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2818         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2819         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2820
2821 #if 0
2822         label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2823         gtk_widget_show (label_format);
2824         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2825                           (GtkAttachOptions) (GTK_FILL),
2826                           (GtkAttachOptions) (0), 0, 0);
2827 #endif
2828
2829         label_format = gtk_label_new ("Format: ");
2830         gtk_widget_show (label_format);
2831         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2832                           (GtkAttachOptions) (GTK_FILL),
2833                           (GtkAttachOptions) (0), 0, 0);
2834
2835         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2836
2837         raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2838         gtk_widget_show (raw_rb);
2839         gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2840                           (GtkAttachOptions) (GTK_FILL),
2841                           (GtkAttachOptions) (0), 0, 0);
2842
2843
2844         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2845         gtk_widget_show (au_rb);
2846         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2847                           (GtkAttachOptions) (GTK_FILL),
2848                           (GtkAttachOptions) (0), 0, 0);
2849
2850 #if 0
2851         /* we support .au - ulaw*/
2852         wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
2853         gtk_widget_show (wav_rb);
2854         gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2855                           (GtkAttachOptions) (GTK_FILL),
2856                           (GtkAttachOptions) (0), 0, 0);
2857
2858         sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit  ");
2859         gtk_widget_show (sw_rb);
2860         gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2861                           (GtkAttachOptions) (GTK_FILL),
2862                           (GtkAttachOptions) (0), 0, 0);
2863         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2864         gtk_widget_show (au_rb);
2865         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2866                           (GtkAttachOptions) (GTK_FILL),
2867                           (GtkAttachOptions) (0), 0, 0);
2868 #endif
2869
2870         channels_label = gtk_label_new ("Channels:    ");
2871         gtk_widget_show (channels_label);
2872         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2873                           (GtkAttachOptions) (GTK_FILL),
2874                           (GtkAttachOptions) (0), 0, 0);
2875         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2876
2877         forward_rb = gtk_radio_button_new_with_label (NULL, "forward    ");
2878         gtk_widget_show (forward_rb);
2879         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2880                           (GtkAttachOptions) (GTK_FILL),
2881                           (GtkAttachOptions) (0), 0, 0);
2882
2883         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
2884         gtk_widget_show (reversed_rb);
2885         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2886                           (GtkAttachOptions) (GTK_FILL),
2887                           (GtkAttachOptions) (0), 0, 0);
2888
2889         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2890         gtk_widget_show (both_rb);
2891         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2892                           (GtkAttachOptions) (GTK_FILL),
2893                           (GtkAttachOptions) (0), 0, 0);
2894
2895         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2896
2897 #if 0
2898         /* if one direction is nok we don't allow saving
2899         XXX this is not ok since the user can click the refresh button and cause changes
2900         but we can not update this window. So we move all the decision on the time the ok
2901         button is clicked */
2902         if (user_data->forward.saved == FALSE) {
2903                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2904                 gtk_widget_set_sensitive(forward_rb, FALSE);
2905                 gtk_widget_set_sensitive(both_rb, FALSE);
2906         }
2907         else if (user_data->reversed.saved == FALSE) {
2908                 gtk_widget_set_sensitive(reversed_rb, FALSE);
2909                 gtk_widget_set_sensitive(both_rb, FALSE);
2910         }
2911 #endif
2912
2913         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2914         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2915         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2916         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2917         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2918         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2919         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2920         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2921
2922         g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2923                          G_CALLBACK(window_delete_event_cb), NULL);
2924         g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2925                          G_CALLBACK(save_voice_as_destroy_cb), user_data);
2926
2927         gtk_widget_show(user_data->dlg.save_voice_as_w);
2928         window_present(user_data->dlg.save_voice_as_w);
2929
2930         /* "Run" the GtkFileChooserDialog.                                              */
2931         /* Upon exit: If "Accept" run the OK callback.                                  */
2932         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
2933         /*            Destroy the window.                                               */
2934         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
2935         /*      return with a TRUE status so that the dialog window will be destroyed.  */
2936         /*      Trying to re-run the dialog after popping up an alert box will not work */
2937         /*       since the user will not be able to dismiss the alert box.              */
2938         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
2939         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
2940         /*                                                                              */
2941         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
2942         /*            GtkFileChooserDialog.                                             */
2943         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2944                 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2945                         break;  /* we're done */
2946                 }
2947         }
2948         window_destroy(user_data->dlg.save_voice_as_w);
2949 }
2950
2951
2952 /****************************************************************************/
2953 /* when we are finished with redisection, we add the label for the statistic */
2954 static void draw_stat(user_data_t *user_data)
2955 {
2956         gchar label_max[200];
2957
2958         g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2959                 user_data->forward.statinfo.total_nr,
2960                 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2961
2962         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2963
2964         g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2965                 user_data->reversed.statinfo.total_nr,
2966                 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2967
2968         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2969
2970         return ;
2971 }
2972
2973
2974
2975 /****************************************************************************/
2976 /* append a line to list */
2977 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2978                         double delta, double jitter, double bandwidth, gchar *status,
2979                         gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2980 {
2981         GtkListStore *list_store;
2982
2983         if (strcmp(status, OK_TEXT) != 0) {
2984                 user_data->dlg.number_of_nok++;
2985         }
2986
2987         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2988
2989         /* Creates a new row at position. iter will be changed to point to this new row.
2990          * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2991          * The row will be filled with the values given to this function.
2992          * :
2993          * should generally be preferred when inserting rows in a sorted list store.
2994          */
2995         gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2996                              PACKET_COLUMN, number,
2997                              DELTA_COLUMN, delta,
2998                              JITTER_COLUMN, jitter,
2999                              IPBW_COLUMN, bandwidth,
3000                              STATUS_COLUMN, (char *)status,
3001                              DATE_COLUMN,  (char *)timeStr,
3002                              LENGTH_COLUMN,     pkt_len,
3003                              FOREGROUND_COLOR_COL, NULL,
3004                              BACKGROUND_COLOR_COL, (char *)color_str,
3005                              -1);
3006
3007         if(flags & STAT_FLAG_FIRST){
3008                 /* Set first row as active */
3009                 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3010         }
3011 }
3012
3013 /****************************************************************************
3014  * Functions needed to present values from the list
3015  */
3016
3017 /* Present floats with two decimals */
3018 static void
3019 iax2_float_data_func (GtkTreeViewColumn *column _U_,
3020                       GtkCellRenderer   *renderer,
3021                       GtkTreeModel      *model,
3022                       GtkTreeIter       *iter,
3023                       gpointer           user_data)
3024 {
3025         gfloat  float_val;
3026         gchar   buf[20];
3027         char   *savelocale;
3028
3029         /* the col to get data from is in userdata */
3030         gint float_col = GPOINTER_TO_INT(user_data);
3031
3032         gtk_tree_model_get(model, iter, float_col, &float_val, -1);
3033
3034         /* save the current locale */
3035         savelocale = setlocale(LC_NUMERIC, NULL);
3036         /* switch to "C" locale to avoid problems with localized decimal separators
3037          * in g_snprintf("%f") functions
3038          */
3039         setlocale(LC_NUMERIC, "C");
3040
3041         g_snprintf(buf, sizeof(buf), "%.2f", float_val);
3042         /* restore previous locale setting */
3043         setlocale(LC_NUMERIC, savelocale);
3044
3045         g_object_set(renderer, "text", buf, NULL);
3046 }
3047
3048
3049 /* Create list */
3050 static
3051 GtkWidget* create_list(user_data_t* user_data)
3052 {
3053
3054         GtkListStore      *list_store;
3055         GtkWidget         *list;
3056         GtkTreeViewColumn *column;
3057         GtkCellRenderer   *renderer;
3058         GtkTreeSortable   *sortable;
3059         GtkTreeView       *list_view;
3060         GtkTreeSelection  *selection;
3061
3062         /* Create the store */
3063         list_store = gtk_list_store_new(N_COLUMN,       /* Total number of columns XXX  */
3064                                         G_TYPE_UINT,    /* Packet                       */
3065                                         G_TYPE_FLOAT,   /* Delta(ms)                    */
3066                                         G_TYPE_FLOAT,   /* Jitter(ms)                   */
3067                                         G_TYPE_FLOAT,   /* IP BW(kbps)                  */
3068                                         G_TYPE_STRING,  /* Status                       */
3069                                         G_TYPE_STRING,  /* Date                         */
3070                                         G_TYPE_UINT,    /* Length                       */
3071                                         G_TYPE_STRING,  /* Foreground color             */
3072                                         G_TYPE_STRING); /* Background color             */
3073
3074         /* Create a view */
3075         list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3076
3077         list_view = GTK_TREE_VIEW(list);
3078         sortable = GTK_TREE_SORTABLE(list_store);
3079
3080         /* Speed up the list display */
3081         gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3082
3083         /* Setup the sortable columns */
3084         gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3085         gtk_tree_view_set_headers_clickable(list_view, FALSE);
3086
3087         /* The view now holds a reference.  We can get rid of our own reference */
3088         g_object_unref (G_OBJECT (list_store));
3089
3090         /*
3091          * Create the first column packet, associating the "text" attribute of the
3092          * cell_renderer to the first column of the model
3093          */
3094         renderer = gtk_cell_renderer_text_new ();
3095         column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3096                                                            "text",      PACKET_COLUMN,
3097                                                            "foreground", FOREGROUND_COLOR_COL,
3098                                                            "background", BACKGROUND_COLOR_COL,
3099                                                            NULL);
3100         gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3101         gtk_tree_view_column_set_resizable(column, TRUE);
3102         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3103         gtk_tree_view_column_set_min_width(column, 100);
3104
3105         /* Add the column to the view. */
3106         gtk_tree_view_append_column (list_view, column);
3107
3108         /* Second column.. Delta(ms). */
3109         renderer = gtk_cell_renderer_text_new ();
3110         column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3111                                                            "text", DELTA_COLUMN,
3112                                                            "foreground", FOREGROUND_COLOR_COL,
3113                                                            "background", BACKGROUND_COLOR_COL,
3114                                                            NULL);
3115
3116         gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3117                                                 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3118
3119         gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3120         gtk_tree_view_column_set_resizable(column, TRUE);
3121         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3122         gtk_tree_view_column_set_min_width(column, 100);
3123         gtk_tree_view_append_column (list_view, column);
3124
3125         /* Third column.. Jitter(ms). */
3126         renderer = gtk_cell_renderer_text_new ();
3127         column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3128                                                            "text", JITTER_COLUMN,
3129                                                            "foreground", FOREGROUND_COLOR_COL,
3130                                                            "background", BACKGROUND_COLOR_COL,
3131                                                            NULL);
3132
3133         gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3134                                                 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3135
3136         gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3137         gtk_tree_view_column_set_resizable(column, TRUE);
3138         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3139         gtk_tree_view_column_set_min_width(column, 100);
3140         gtk_tree_view_append_column (list_view, column);
3141
3142         /* Fourth column.. IP BW(kbps). */
3143         renderer = gtk_cell_renderer_text_new ();
3144         column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3145                                                            "text", IPBW_COLUMN,
3146                                                            "foreground", FOREGROUND_COLOR_COL,
3147                                                            "background", BACKGROUND_COLOR_COL,
3148                                                            NULL);
3149
3150         gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3151                                                 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3152
3153         gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3154         gtk_tree_view_column_set_resizable(column, TRUE);
3155         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3156         gtk_tree_view_column_set_min_width(column, 100);
3157         gtk_tree_view_append_column (list_view, column);
3158
3159         /* Fifth column..  Status. */
3160         renderer = gtk_cell_renderer_text_new ();
3161         column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3162                                                             "text", STATUS_COLUMN,
3163                                                             "foreground", FOREGROUND_COLOR_COL,
3164                                                             "background", BACKGROUND_COLOR_COL,
3165                                                             NULL);
3166         gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3167         gtk_tree_view_column_set_resizable(column, TRUE);
3168         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3169         gtk_tree_view_column_set_min_width(column, 100);
3170         gtk_tree_view_append_column (list_view, column);
3171
3172         /* Sixth column.. Length. */
3173         renderer = gtk_cell_renderer_text_new ();
3174         column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3175                                                            "text", LENGTH_COLUMN,
3176                                                            "foreground", FOREGROUND_COLOR_COL,
3177                                                            "background", BACKGROUND_COLOR_COL,
3178                                                            NULL);
3179
3180
3181         gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3182         gtk_tree_view_column_set_resizable(column, TRUE);
3183         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3184         gtk_tree_view_column_set_min_width(column, 100);
3185         gtk_tree_view_append_column (list_view, column);
3186
3187         /* Now enable the sorting of each column */
3188         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3189         gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3190
3191         /* Setup the selection handler */
3192         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3193         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3194
3195         g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3196                           G_CALLBACK (on_list_select_row),
3197                           user_data);
3198         return list;
3199 }
3200
3201
3202
3203 /****************************************************************************/
3204 /* Create the dialog box with all widgets */
3205 static void create_iax2_dialog(user_data_t* user_data)
3206 {
3207         GtkWidget *window = NULL;
3208         GtkWidget *list_fwd;
3209         GtkWidget *list_rev;
3210         GtkWidget *label_stats_fwd;
3211         GtkWidget *label_stats_rev;
3212         GtkWidget *notebook;
3213
3214         GtkWidget *main_vb, *page, *page_r;
3215         GtkWidget *label;
3216         GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3217         GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3218         GtkWidget *graph_bt;
3219         gchar label_forward[150];
3220         gchar label_reverse[150];
3221
3222         gchar str_ip_src[16];
3223         gchar str_ip_dst[16];
3224
3225         window = dlg_window_new("Wireshark: IAX2 Stream Analysis");  /* transient_for top_level */
3226         gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3227
3228         /* Container for each row of widgets */
3229         main_vb = gtk_vbox_new(FALSE, 2);
3230         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3231         gtk_container_add(GTK_CONTAINER(window), main_vb);
3232         gtk_widget_show(main_vb);
3233
3234         /* Notebooks... */
3235         g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3236         g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3237
3238         g_snprintf(label_forward, sizeof(label_forward),
3239                 "Analysing stream from  %s port %u  to  %s port %u  ",
3240                 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3241
3242
3243         g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3244         g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3245
3246         g_snprintf(label_reverse, sizeof(label_reverse),
3247                 "Analysing stream from  %s port %u  to  %s port %u  ",
3248                 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3249
3250         /* Start a notebook for flipping between sets of changes */
3251         notebook = gtk_notebook_new();
3252         gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3253         g_object_set_data(G_OBJECT(window), "notebook", notebook);
3254
3255         user_data->dlg.notebook_signal_id =
3256                 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3257
3258         /* page for forward connection */
3259         page = gtk_vbox_new(FALSE, 8);
3260         gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3261
3262         /* direction label */
3263         label = gtk_label_new(label_forward);
3264         gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3265
3266         /* place for some statistics */
3267         label_stats_fwd = gtk_label_new("\n");
3268         gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3269
3270         /* scrolled window */
3271         scrolled_window = scrolled_window_new(NULL, NULL);
3272
3273         /* packet list */
3274         list_fwd = create_list(user_data);
3275         gtk_widget_show(list_fwd);
3276         gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3277         gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3278         gtk_widget_show(scrolled_window);
3279
3280         /* tab */
3281         label = gtk_label_new("  Forward Direction  ");
3282         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3283
3284         /* same page for reversed connection */
3285         page_r = gtk_vbox_new(FALSE, 8);
3286         gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3287         label = gtk_label_new(label_reverse);
3288         gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3289         label_stats_rev = gtk_label_new("\n");
3290         gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3291
3292         scrolled_window_r = scrolled_window_new(NULL, NULL);
3293
3294         list_rev = create_list(user_data);
3295         gtk_widget_show(list_rev);
3296         gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3297         gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3298         gtk_widget_show(scrolled_window_r);
3299
3300         label = gtk_label_new("  Reversed Direction  ");
3301         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3302
3303 #if 0
3304         /* page for help&about or future */
3305         page_help = gtk_hbox_new(FALSE, 5);
3306         label = gtk_label_new("     Future    ");
3307         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3308         frame = gtk_frame_new("");
3309         text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3310         gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3311         gtk_container_add(GTK_CONTAINER(frame), text);
3312         gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3313         gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3314 #endif
3315
3316         /* show all notebooks */
3317         gtk_widget_show_all(notebook);
3318
3319         /* buttons */
3320         box4 = gtk_hbutton_box_new();
3321         gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3322         gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3323         gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3324         gtk_box_set_spacing(GTK_BOX (box4), 0);
3325         gtk_widget_show(box4);
3326
3327         voice_bt = gtk_button_new_with_label("Save payload...");
3328         gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3329         gtk_widget_show(voice_bt);
3330         g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3331
3332         csv_bt = gtk_button_new_with_label("Save as CSV...");
3333         gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3334         gtk_widget_show(csv_bt);
3335         g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3336
3337         refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3338         gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3339         gtk_widget_show(refresh_bt);
3340         g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3341
3342         goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3343         gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3344         gtk_widget_show(goto_bt);
3345         g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3346
3347         graph_bt = gtk_button_new_with_label("Graph");
3348         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3349         gtk_widget_show(graph_bt);
3350         g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3351
3352         next_bt = gtk_button_new_with_label("Next non-Ok");
3353         gtk_container_add(GTK_CONTAINER(box4), next_bt);
3354         gtk_widget_show(next_bt);
3355         g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3356
3357         close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3358         gtk_container_add(GTK_CONTAINER(box4), close_bt);
3359 #if GTK_CHECK_VERSION(2,18,0)
3360         gtk_widget_set_can_default(close_bt, TRUE);
3361 #else
3362         GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3363 #endif
3364         gtk_widget_show(close_bt);
3365         window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3366
3367         g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3368         g_signal_connect(window, "destroy", G_CALLBACK(on_iax2_window_destroy), user_data);
3369
3370         gtk_widget_show(window);
3371         window_present(window);
3372
3373         /* some widget references need to be saved for outside use */
3374         user_data->dlg.window = window;
3375         user_data->dlg.list_fwd = list_fwd;
3376         user_data->dlg.list_rev = list_rev;
3377         user_data->dlg.label_stats_fwd = label_stats_fwd;
3378         user_data->dlg.label_stats_rev = label_stats_rev;
3379         user_data->dlg.notebook = notebook;
3380         user_data->dlg.selected_list = list_fwd;
3381         user_data->dlg.number_of_nok = 0;
3382
3383         /*
3384          * select the initial row
3385          */
3386         gtk_widget_grab_focus(list_fwd);
3387 }
3388
3389 #if 0
3390 /****************************************************************************/
3391 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3392                                                         const gchar* proto_field, guint32* p_result)
3393 {
3394         field_info            *finfo;
3395         proto_node            *proto_sibling_node;
3396         header_field_info     *hfssrc;
3397         ipv4_addr             *ipv4;
3398
3399         finfo = PNODE_FINFO(ptree_node);
3400
3401         if (hfinformation==(finfo->hfinfo)) {
3402                 hfssrc = proto_registrar_get_byname(proto_field);
3403                 if (hfssrc == NULL) {
3404                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3405                                       "Bad field name!");
3406                         return FALSE;
3407                         }
3408                 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3409                                         ptree_node=ptree_node->next) {
3410                         finfo=PNODE_FINFO(ptree_node);
3411                         if (hfssrc==finfo->hfinfo) {
3412                                 if (hfinformation->type==FT_IPv4) {
3413                                         ipv4 = fvalue_get(&finfo->value);
3414                                         *p_result = ipv4_get_net_order_addr(ipv4);
3415                                 }
3416                                 else {
3417                                         *p_result = fvalue_get_uinteger(&finfo->value);
3418                                 }
3419                                 return TRUE;
3420                         }
3421                 }
3422                 if(!ptree_node)
3423                         return FALSE;
3424         }
3425
3426         proto_sibling_node = ptree_node->next;
3427
3428         if (proto_sibling_node) {
3429                 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3430         }
3431         else
3432         return FALSE;
3433 }
3434
3435 /****************************************************************************/
3436 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3437                                                  const gchar* proto_name,
3438                                                  const gchar* proto_field,
3439                                                  guint32* p_result)
3440 {
3441         proto_node      *ptree_node;
3442         header_field_info     *hfinformation;
3443
3444         hfinformation = proto_registrar_get_byname(proto_name);
3445         if (hfinformation == NULL) {
3446                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3447                               "Bad proto!");
3448                 return FALSE;
3449                 }
3450
3451         ptree_node = ((proto_node *)protocol_tree)->first_child;
3452         if (!ptree_node) {
3453                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3454                               "No info!");
3455                 return FALSE;
3456                 }
3457         return process_node(ptree_node, hfinformation, proto_field, p_result);
3458 }
3459 #endif
3460
3461 /****************************************************************************/
3462 void iax2_analysis(
3463                 address *ip_src_fwd,
3464                 guint16 port_src_fwd,
3465                 address *ip_dst_fwd,
3466                 guint16 port_dst_fwd,
3467                 address *ip_src_rev,
3468                 guint16 port_src_rev,
3469                 address *ip_dst_rev,
3470                 guint16 port_dst_rev
3471                 )
3472 {
3473         user_data_t *user_data;
3474         int fd;
3475         int i;
3476         static GdkColor col[MAX_GRAPHS] = {
3477                 {0,     0x0000, 0x0000, 0x0000},
3478                 {0,     0xffff, 0x0000, 0x0000},
3479                 {0,     0x0000, 0xffff, 0x0000},
3480                 {0,     0x0000, 0x0000, 0xffff}
3481         };
3482         char *tempname;
3483
3484         /* init */
3485         user_data = g_malloc(sizeof(user_data_t));
3486
3487         COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3488         user_data->port_src_fwd = port_src_fwd;
3489         COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3490         user_data->port_dst_fwd = port_dst_fwd;
3491         COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3492         user_data->port_src_rev = port_src_rev;
3493         COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3494         user_data->port_dst_rev = port_dst_rev;
3495
3496
3497         /* file names for storing sound data */
3498         /*XXX: check for errors*/
3499         fd = create_tempfile(&tempname, "wireshark_iax2_f");
3500         user_data->f_tempname = g_strdup(tempname);
3501         ws_close(fd);
3502         fd = create_tempfile(&tempname, "wireshark_iax2_r");
3503         user_data->r_tempname = g_strdup(tempname);
3504         ws_close(fd);
3505         user_data->forward.saveinfo.fp = NULL;
3506         user_data->reversed.saveinfo.fp = NULL;
3507         user_data->dlg.save_voice_as_w = NULL;
3508         user_data->dlg.save_csv_as_w = NULL;
3509         user_data->dlg.dialog_graph.window = NULL;
3510
3511         /* init dialog_graph */
3512         user_data->dlg.dialog_graph.needs_redraw=TRUE;
3513         user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
3514         user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
3515         user_data->dlg.dialog_graph.draw_area=NULL;
3516 #if GTK_CHECK_VERSION(2,22,0)
3517         user_data->dlg.dialog_graph.surface=NULL;
3518 #else
3519         user_data->dlg.dialog_graph.pixmap=NULL;
3520 #endif
3521         user_data->dlg.dialog_graph.scrollbar=NULL;
3522         user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3523         user_data->dlg.dialog_graph.surface_width=500;
3524         user_data->dlg.dialog_graph.surface_height=200;
3525         user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
3526         user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3527         user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
3528         user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3529         user_data->dlg.dialog_graph.last_interval=0xffffffff;
3530         user_data->dlg.dialog_graph.max_interval=0;
3531         user_data->dlg.dialog_graph.num_items=0;
3532         user_data->dlg.dialog_graph.start_time = -1;
3533
3534         for(i=0;i<MAX_GRAPHS;i++){
3535                 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3536                 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3537                 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3538                 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3539                 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3540                 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3541                 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3542         }
3543
3544         /* create the dialog box */
3545         create_iax2_dialog(user_data);
3546
3547         /* proceed as if the Refresh button would have been pressed */
3548         on_refresh_bt_clicked(NULL, user_data);
3549 }
3550
3551 /****************************************************************************/
3552 /* entry point from main menu */
3553 #ifdef MAIN_MENU_USE_UIMANAGER
3554 void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3555 #else
3556 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3557 #endif
3558 {
3559         address ip_src_fwd;
3560         guint16 port_src_fwd;
3561         address ip_dst_fwd;
3562         guint16 port_dst_fwd;
3563         address ip_src_rev;
3564         guint16 port_src_rev;
3565         address ip_dst_rev;
3566         guint16 port_dst_rev;
3567         /* unsigned int ptype; */
3568
3569         gchar filter_text[256];
3570         dfilter_t *sfcode;
3571         capture_file *cf;
3572         epan_dissect_t edt;
3573         gboolean frame_matched;
3574         frame_data *fdata;
3575         GList *strinfo_list;
3576         GList *filtered_list = NULL;
3577         rtp_stream_info_t *strinfo;
3578         guint nfound;
3579
3580         /* Try to compile the filter. */
3581         g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3582         if (!dfilter_compile(filter_text, &sfcode)) {
3583                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3584                 return;
3585         }
3586         /* we load the current file into cf variable */
3587         cf = &cfile;
3588         fdata = cf->current_frame;
3589
3590         /* we are on the selected frame now */
3591         if (fdata == NULL)
3592                 return; /* if we exit here it's an error */
3593
3594         /* dissect the current frame */
3595         if (!cf_read_frame(cf, fdata))
3596                 return; /* error reading the frame */
3597         epan_dissect_init(&edt, TRUE, FALSE);
3598         epan_dissect_prime_dfilter(&edt, sfcode);
3599         epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3600
3601         /* if it is not an iax2 frame, show an error dialog */
3602         frame_matched = dfilter_apply_edt(sfcode, &edt);
3603         if (frame_matched != 1) {
3604                 epan_dissect_cleanup(&edt);
3605                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3606                     "You didn't choose a IAX2 packet!");
3607                 return;
3608         }
3609         /* check if it is Voice or MiniPacket
3610         if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3611                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3612                     "Please select a Voice packet!");
3613                 return;
3614         } */
3615
3616         /* check if it is part of a Call */
3617         if (edt.pi.circuit_id == 0) {
3618                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3619                     "Please select a Call packet!");
3620                 return;
3621         }
3622
3623         /* ok, it is a IAX2 frame, so let's get the ip and port values */
3624         COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3625         COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3626         port_src_fwd = edt.pi.srcport;
3627         port_dst_fwd = edt.pi.destport;
3628
3629         /* assume the inverse ip/port combination for the reverse direction */
3630         COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3631         COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3632         port_src_rev = edt.pi.destport;
3633         port_dst_rev = edt.pi.srcport;
3634
3635         /* Scan for rtpstream */
3636         rtpstream_scan();
3637         /* search for reversed direction in the global rtp streams list */
3638         nfound = 0;
3639         strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3640         while (strinfo_list)
3641         {
3642                 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3643                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3644                         && strinfo->src_port==port_src_fwd
3645                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3646                         && strinfo->dest_port==port_dst_fwd)
3647                 {
3648                         filtered_list = g_list_prepend(filtered_list, strinfo);
3649                 }
3650
3651                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3652                         && strinfo->src_port==port_src_rev
3653                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3654                         && strinfo->dest_port==port_dst_rev)
3655                 {
3656                         ++nfound;
3657                         filtered_list = g_list_append(filtered_list, strinfo);
3658                 }
3659
3660                 strinfo_list = g_list_next(strinfo_list);
3661         }
3662
3663         /* if more than one reverse streams found, we let the user choose the right one */
3664         if (nfound>1) {
3665                 rtpstream_dlg_show(filtered_list);
3666                 return;
3667         }
3668         else {
3669                 iax2_analysis(
3670                         &ip_src_fwd,
3671                         port_src_fwd,
3672                         &ip_dst_fwd,
3673                         port_dst_fwd,
3674                         &ip_src_rev,
3675                         port_src_rev,
3676                         &ip_dst_rev,
3677                         port_dst_rev
3678                         );
3679         }
3680 }
3681
3682 /****************************************************************************/
3683 static void
3684 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3685 {
3686         iax2_analysis_cb(NULL, NULL);
3687 }
3688
3689 /****************************************************************************/
3690 void
3691 register_tap_listener_iax2_analysis(void)
3692 {
3693         register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3694
3695 #ifdef MAIN_MENU_USE_UIMANAGER
3696 #else
3697         register_stat_menu_item("IA_X2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3698             iax2_analysis_cb, NULL, NULL, NULL);
3699 #endif
3700 }
3701
3702
3703 /*
3704  * Editor modelines
3705  *
3706  * Local Variables:
3707  * c-basic-offset: 8
3708  * tab-width: 8
3709  * indent-tabs-mode: t
3710  * End:
3711  *
3712  * ex: set shiftwidth=8 tabstop=8 noexpandtab
3713  * :indentSize=8:tabSize=8:noTabs=false:
3714  */