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