Fix the wireless settings button for AirPCap devices in the
[obnox/wireshark/wip.git] / gtk / rtp_analysis.c
1 /* rtp_analysis.c
2  * RTP analysis addition for Wireshark
3  *
4  * $Id$
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * based on tap_rtp.c
10  * Copyright 2003, Iskratel, Ltd, Kranj
11  * By Miha Jemec <m.jemec@iskratel.si>
12  *
13  * Graph. Copyright 2004, Verso Technology
14  * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15  * Based on io_stat.c by Ronnie Sahlberg
16  *
17  * Wireshark - Network traffic analyzer
18  * By Gerald Combs <gerald@wireshark.org>
19  * Copyright 1998 Gerald Combs
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License
23  * as published by the Free Software Foundation; either version 2
24  * of the License, or (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program; if not, write to the Free Software
33  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <stdio.h>
41 #include <math.h>
42 #include <string.h>
43 #include <locale.h>
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48
49 #ifdef HAVE_FCNTL_H
50 #include <fcntl.h>
51 #endif
52
53 #include <gtk/gtk.h>
54
55 #include <epan/epan_dissect.h>
56 #include <epan/filesystem.h>
57 #include <epan/pint.h>
58 #include <epan/tap.h>
59 #include <epan/dissectors/packet-rtp.h>
60 #include <epan/rtp_pt.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
64
65 #include "../util.h"
66 #include "../g711.h"
67 #include "../alert_box.h"
68 #include "../simple_dialog.h"
69 #include "../stat_menu.h"
70 #include "../progress_dlg.h"
71 #include "../tempfile.h"
72 #include <wsutil/file_util.h>
73
74 #include "gtk/gtkglobals.h"
75 #include "gtk/dlg_utils.h"
76 #include "gtk/file_dlg.h"
77 #include "gtk/gui_utils.h"
78 #include "gtk/gui_stat_menu.h"
79 #include "gtk/pixmap_save.h"
80 #include "gtk/main.h"
81 #include "gtk/rtp_analysis.h"
82 #include "gtk/rtp_stream.h"
83 #include "gtk/rtp_stream_dlg.h"
84 #include "gtk/stock_icons.h"
85 #include "gtk/utf8_entities.h"
86
87 #ifdef HAVE_LIBPORTAUDIO
88 #include "gtk/graph_analysis.h"
89 #include "gtk/voip_calls.h"
90 #include "gtk/rtp_player.h"
91 #endif /* HAVE_LIBPORTAUDIO */
92
93 #include "gtk/old-gtk-compat.h"
94
95 enum
96 {
97         PACKET_COLUMN,
98         SEQUENCE_COLUMN,
99         TIMESTAMP_COLUMN,
100         DELTA_COLUMN,
101         JITTER_COLUMN,
102         SKEW_COLUMN,
103         IPBW_COLUMN,
104         MARKER_COLUMN,
105         STATUS_COLUMN,
106         DATE_COLUMN,
107         LENGTH_COLUMN,
108         FOREGROUND_COLOR_COL,
109         BACKGROUND_COLOR_COL,
110         N_COLUMN /* The number of columns */
111 };
112 /****************************************************************************/
113
114 #define NUM_COLS 9
115 #define NUM_GRAPH_ITEMS 100000
116 #define MAX_YSCALE 16
117 #define AUTO_MAX_YSCALE_INDEX 0
118 #define AUTO_MAX_YSCALE 0
119 #define MAX_GRAPHS 6
120 #define GRAPH_FWD_JITTER 0
121 #define GRAPH_FWD_DIFF 1
122 #define GRAPH_FWD_DELTA 2
123 #define GRAPH_REV_JITTER 3
124 #define GRAPH_REV_DIFF 4
125 #define GRAPH_REV_DELTA 5
126 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
127
128 #define MAX_PIXELS_PER_TICK 4
129 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
130 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
131 static const char *graph_descr[MAX_GRAPHS] = {"Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"};
132 /* unit is in ms */
133 #define MAX_TICK_VALUES 5
134 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
135 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
136 typedef struct _dialog_graph_graph_item_t {
137         guint32 value;
138         guint32 flags;
139 } dialog_graph_graph_item_t;
140
141 typedef struct _dialog_graph_graph_t {
142         struct _user_data_t *ud;
143         dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
144         int plot_style;
145         gboolean display;
146         GtkWidget *display_button;
147         int hf_index;
148 #if GTK_CHECK_VERSION(3,0,0)
149         GdkRGBA rgba_color;
150 #endif
151         GdkColor color;
152         gchar title[100];
153 } dialog_graph_graph_t;
154
155
156 typedef struct _dialog_graph_t {
157         gboolean needs_redraw;
158         gint32 interval_index;  /* index into tick_interval_values_array */
159         gint32 interval;        /* measurement interval in ms */
160         guint32 last_interval;
161         guint32 max_interval;  /* XXX max_interval and num_items are redundant */
162         guint32 num_items;
163         struct _dialog_graph_graph_t graph[MAX_GRAPHS];
164         GtkWidget *window;
165         GtkWidget *draw_area;
166 #if GTK_CHECK_VERSION(2,22,0)
167         cairo_surface_t *surface;
168 #else
169         GdkPixmap *pixmap;
170 #endif
171         GtkAdjustment *scrollbar_adjustment;
172         GtkWidget *scrollbar;
173         int surface_width;
174         int surface_height;
175         int pixels_per_tick_index; /* index into pixels_per_tick array */
176         int pixels_per_tick;
177         int max_y_units_index;     /* index into yscale_max array      */
178         int max_y_units;
179         double start_time;
180 } dialog_graph_t;
181
182 typedef struct _dialog_data_t {
183         GtkWidget *window;
184         GtkWidget *list_fwd;
185         GtkTreeIter  iter;
186         GtkWidget *list_rev;
187         GtkWidget *label_stats_fwd;
188         GtkWidget *label_stats_rev;
189         GtkWidget *selected_list;
190         guint   number_of_nok;
191         GtkTreeSelection *selected_list_sel;
192         gint selected_list_row;
193         GtkWidget *notebook;
194         GtkWidget *save_voice_as_w;
195         GtkWidget *save_csv_as_w;
196         gint notebook_signal_id;
197         dialog_graph_t dialog_graph;
198 } dialog_data_t;
199
200 #define OK_TEXT "[ Ok ]"
201
202 /* type of error when saving voice in a file didn't succeed */
203 typedef enum {
204         TAP_RTP_WRONG_CODEC,
205         TAP_RTP_WRONG_LENGTH,
206         TAP_RTP_PADDING_ERROR,
207         TAP_RTP_SHORT_FRAME,
208         TAP_RTP_FILE_OPEN_ERROR,
209         TAP_RTP_FILE_WRITE_ERROR,
210         TAP_RTP_NO_DATA
211 } error_type_t;
212
213 typedef struct _tap_rtp_save_info_t {
214         FILE *fp;
215         guint32 count;
216         error_type_t error_type;
217         gboolean saved;
218 } tap_rtp_save_info_t;
219
220
221 /* structure that holds the information about the forward and reversed direction */
222 struct _info_direction {
223         tap_rtp_stat_t statinfo;
224         tap_rtp_save_info_t saveinfo;
225 };
226
227 #define SILENCE_PCMU    (guint8)0xFF
228 #define SILENCE_PCMA    (guint8)0x55
229
230 /* structure that holds general information about the connection
231 * and structures for both directions */
232 typedef struct _user_data_t {
233         /* tap associated data*/
234         address ip_src_fwd;
235         guint16 port_src_fwd;
236         address ip_dst_fwd;
237         guint16 port_dst_fwd;
238         guint32 ssrc_fwd;
239         address ip_src_rev;
240         guint16 port_src_rev;
241         address ip_dst_rev;
242         guint16 port_dst_rev;
243         guint32 ssrc_rev;
244
245         struct _info_direction forward;
246         struct _info_direction reversed;
247
248         char *f_tempname;
249         char *r_tempname;
250
251         /* dialog associated data */
252         dialog_data_t dlg;
253
254 } user_data_t;
255
256
257 /* Column titles. */
258 static const gchar *titles[11] =  {
259         "Packet",
260         "Sequence",
261         "Time stamp",
262         "Delta (ms)",
263         "Jitter (ms)",
264         "Skew(ms)",
265         "IP BW (kbps)",
266         "Marker",
267         "Status",
268         "Date",
269         "Length"
270 };
271
272 #define SAVE_FORWARD_DIRECTION_MASK 0x01
273 #define SAVE_REVERSE_DIRECTION_MASK 0x02
274 #define SAVE_BOTH_DIRECTION_MASK        (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
275
276 #define SAVE_NONE_FORMAT 0
277 #define SAVE_WAV_FORMAT 1
278 #define SAVE_AU_FORMAT  2
279 #define SAVE_SW_FORMAT  3
280 #define SAVE_RAW_FORMAT 4
281
282
283 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
284 /****************************************************************************/
285 static void enable_graph(dialog_graph_graph_t *dgg)
286 {
287
288         dgg->display=TRUE;
289
290 }
291
292 static void dialog_graph_reset(user_data_t* user_data);
293
294
295
296 /****************************************************************************/
297 /* TAP FUNCTIONS */
298
299 /****************************************************************************/
300 /* when there is a [re]reading of packet's */
301 static void
302 rtp_reset(void *user_data_arg)
303 {
304         user_data_t *user_data = user_data_arg;
305         user_data->forward.statinfo.first_packet = TRUE;
306         user_data->reversed.statinfo.first_packet = TRUE;
307         user_data->forward.statinfo.max_delta = 0;
308         user_data->reversed.statinfo.max_delta = 0;
309         user_data->forward.statinfo.max_jitter = 0;
310         user_data->reversed.statinfo.max_jitter = 0;
311         user_data->forward.statinfo.max_skew = 0;
312         user_data->reversed.statinfo.max_skew = 0;
313         user_data->forward.statinfo.mean_jitter = 0;
314         user_data->reversed.statinfo.mean_jitter = 0;
315         user_data->forward.statinfo.delta = 0;
316         user_data->reversed.statinfo.delta = 0;
317         user_data->forward.statinfo.diff = 0;
318         user_data->reversed.statinfo.diff = 0;
319         user_data->forward.statinfo.jitter = 0;
320         user_data->reversed.statinfo.jitter = 0;
321         user_data->forward.statinfo.skew = 0;
322         user_data->reversed.statinfo.skew = 0;
323         user_data->forward.statinfo.sumt = 0;
324         user_data->reversed.statinfo.sumt = 0;
325         user_data->forward.statinfo.sumTS = 0;
326         user_data->reversed.statinfo.sumTS = 0;
327         user_data->forward.statinfo.sumt2 = 0;
328         user_data->reversed.statinfo.sumt2 = 0;
329         user_data->forward.statinfo.sumtTS = 0;
330         user_data->reversed.statinfo.sumtTS = 0;
331         user_data->forward.statinfo.bandwidth = 0;
332         user_data->reversed.statinfo.bandwidth = 0;
333         user_data->forward.statinfo.total_bytes = 0;
334         user_data->reversed.statinfo.total_bytes = 0;
335         user_data->forward.statinfo.bw_start_index = 0;
336         user_data->reversed.statinfo.bw_start_index = 0;
337         user_data->forward.statinfo.bw_index = 0;
338         user_data->reversed.statinfo.bw_index = 0;
339         user_data->forward.statinfo.timestamp = 0;
340         user_data->reversed.statinfo.timestamp = 0;
341         user_data->forward.statinfo.max_nr = 0;
342         user_data->reversed.statinfo.max_nr = 0;
343         user_data->forward.statinfo.total_nr = 0;
344         user_data->reversed.statinfo.total_nr = 0;
345         user_data->forward.statinfo.sequence = 0;
346         user_data->reversed.statinfo.sequence = 0;
347         user_data->forward.statinfo.start_seq_nr = 0;
348         user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
349         user_data->forward.statinfo.stop_seq_nr = 0;
350         user_data->reversed.statinfo.stop_seq_nr = 0;
351         user_data->forward.statinfo.cycles = 0;
352         user_data->reversed.statinfo.cycles = 0;
353         user_data->forward.statinfo.under = FALSE;
354         user_data->reversed.statinfo.under = FALSE;
355         user_data->forward.statinfo.start_time = 0;
356         user_data->reversed.statinfo.start_time = 0;
357         user_data->forward.statinfo.time = 0;
358         user_data->reversed.statinfo.time = 0;
359         user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
360         user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
361
362         user_data->forward.saveinfo.count = 0;
363         user_data->reversed.saveinfo.count = 0;
364         user_data->forward.saveinfo.saved = FALSE;
365         user_data->reversed.saveinfo.saved = FALSE;
366
367         /* clear the dialog box lists */
368         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
369         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
370
371         /* reset graph info */
372         dialog_graph_reset(user_data);
373
374 #ifdef HAVE_LIBPORTAUDIO
375         /* reset the RTP player */
376         reset_rtp_player();
377 #endif
378         /* XXX check for error at fclose? */
379         if (user_data->forward.saveinfo.fp != NULL)
380                 fclose(user_data->forward.saveinfo.fp);
381         if (user_data->reversed.saveinfo.fp != NULL)
382                 fclose(user_data->reversed.saveinfo.fp);
383         user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
384         if (user_data->forward.saveinfo.fp == NULL)
385                 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
386         user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
387         if (user_data->reversed.saveinfo.fp == NULL)
388                 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
389         return;
390 }
391
392 /****************************************************************************/
393 static gboolean rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
394 {
395         dialog_graph_graph_item_t *it;
396         guint32 idx;
397         double rtp_time;
398
399         /*
400         * We sometimes get called when dgg is disabled.
401         * This is a bug since the tap listener should be removed first
402         */
403         if(!dgg->display){
404                 return FALSE;
405         }
406
407         dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
408
409         /*
410         * Find which interval this is supposed to go in and store the
411         * interval index as idx
412         */
413         if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
414                 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
415         }
416         rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
417         if(rtp_time<0){
418                 return FALSE;
419         }
420         idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
421
422         /* some sanity checks */
423         if(idx>=NUM_GRAPH_ITEMS){
424                 return FALSE;
425         }
426
427         /* update num_items */
428         if(idx > dgg->ud->dlg.dialog_graph.num_items){
429                 dgg->ud->dlg.dialog_graph.num_items=idx;
430                 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
431         }
432
433         /*
434         * Find the appropriate dialog_graph_graph_item_t structure
435         */
436         it=&dgg->items[idx];
437
438         /*
439         * Use the max value to highlight RTP problems
440         */
441         if (value > it->value) {
442                 it->value=value;
443         }
444         it->flags = it->flags | statinfo->flags;
445
446         return TRUE;
447 }
448
449 /****************************************************************************/
450 /* here we can redraw the output */
451 /* not used yet */
452 static void rtp_draw(void *prs _U_)
453 {
454         return;
455 }
456
457 /* forward declarations */
458 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
459                         double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
460                         gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
461
462 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
463         tap_rtp_stat_t *statinfo, packet_info *pinfo,
464         const struct _rtp_info *rtpinfo);
465
466 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
467                                    tap_rtp_stat_t *statinfo,
468                                    packet_info *pinfo,
469                                    const struct _rtp_info *rtpinfo);
470
471
472 /****************************************************************************/
473 /* whenever a RTP packet is seen by the tap listener */
474 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
475 {
476         user_data_t *user_data = user_data_arg;
477         const struct _rtp_info *rtpinfo = rtpinfo_arg;
478         gboolean rtp_selected = FALSE;
479
480         /* we ignore packets that are not displayed */
481         if (pinfo->fd->flags.passed_dfilter == 0)
482                 return 0;
483         /* also ignore RTP Version != 2 */
484         else if (rtpinfo->info_version !=2)
485                 return 0;
486         /* is it the forward direction?  */
487         else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
488                 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
489                 && user_data->port_src_fwd == pinfo->srcport
490                 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
491                 && user_data->port_dst_fwd == pinfo->destport)  {
492                 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
493                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
494                         &(user_data->forward.statinfo), pinfo,
495                         (guint32)(user_data->forward.statinfo.jitter*1000));
496                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
497                         &(user_data->forward.statinfo), pinfo,
498                         (guint32)(user_data->forward.statinfo.diff*1000));
499                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
500                         &(user_data->forward.statinfo), pinfo,
501                         (guint32)(user_data->forward.statinfo.delta*1000));
502                 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
503                         &(user_data->forward.statinfo), pinfo, rtpinfo);
504                 rtp_packet_save_payload(&(user_data->forward.saveinfo),
505                         &(user_data->forward.statinfo), pinfo, rtpinfo);
506                 rtp_selected = TRUE;
507         }
508         /* is it the reversed direction? */
509         else if (user_data->ssrc_rev == rtpinfo->info_sync_src
510                 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
511                 && user_data->port_src_rev == pinfo->srcport
512                 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
513                 && user_data->port_dst_rev == pinfo->destport)  {
514                 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
515                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
516                         &(user_data->reversed.statinfo), pinfo,
517                         (guint32)(user_data->reversed.statinfo.jitter*1000));
518                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
519                         &(user_data->reversed.statinfo), pinfo,
520                         (guint32)(user_data->reversed.statinfo.diff*1000));
521                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
522                         &(user_data->reversed.statinfo), pinfo,
523                         (guint32)(user_data->reversed.statinfo.delta*1000));
524                 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
525                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
526                 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
527                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
528                 rtp_selected = TRUE;
529         }
530         /* add this RTP for future listening using the RTP Player*/
531 #ifdef HAVE_LIBPORTAUDIO
532         if (rtp_selected)
533                 add_rtp_packet(rtpinfo, pinfo);
534 #endif
535
536         return 0;
537 }
538
539 /*
540 Replaced by using the strings instead.
541 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
542 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
543 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
544 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
545 COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
546 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
547 */
548 /****************************************************************************/
549 /* adds statistics information from the packet to the list */
550 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
551         tap_rtp_stat_t *statinfo, packet_info *pinfo,
552         const struct _rtp_info *rtpinfo)
553 {
554         guint16 msecs;
555         gchar timeStr[32];
556         struct tm *tm_tmp;
557         time_t then;
558         gchar status[40];
559         gchar color_str[14];
560         then = pinfo->fd->abs_ts.secs;
561         msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
562         tm_tmp = localtime(&then);
563         g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
564                 tm_tmp->tm_mon + 1,
565                 tm_tmp->tm_mday,
566                 tm_tmp->tm_year + 1900,
567                 tm_tmp->tm_hour,
568                 tm_tmp->tm_min,
569                 tm_tmp->tm_sec,
570                 msecs);
571
572         /* Default to using black on white text if nothing below overrides it */
573         g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
574
575         if (statinfo->pt == PT_CN) {
576                 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
577                 /* color = COLOR_CN; */
578                 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
579         }
580         else if (statinfo->pt == PT_CN_OLD) {
581                 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
582                 /* color = COLOR_CN; */
583                 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
584         }
585         else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
586                 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
587                 /* color = COLOR_ERROR; */
588                 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
589         }
590         else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
591                 if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
592                         g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
593                 }else{
594                         g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
595                 }
596                 /* color = COLOR_WARNING; */
597                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
598         }
599         else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
600                 g_snprintf(status,sizeof(status),"Incorrect timestamp");
601                 /* color = COLOR_WARNING; */
602                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
603         }
604         else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
605                 &&  !(statinfo->flags & STAT_FLAG_FIRST)
606                 &&  !(statinfo->flags & STAT_FLAG_PT_CN)
607                 &&  (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
608                 &&  !(statinfo->flags & STAT_FLAG_MARKER)) {
609                 g_snprintf(status,sizeof(status),"Marker missing?");
610                 /* color = COLOR_WARNING; */
611                 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
612         }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
613                 g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
614                 /* XXX add color? */
615                 /* color = COLOR_T_EVENT; */
616                 g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
617         }else {
618                 if (statinfo->flags & STAT_FLAG_MARKER) {
619                         /* color = COLOR_WARNING; */
620                         g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
621                 }
622                 g_snprintf(status,sizeof(status),OK_TEXT);
623         }
624         /*  is this the first packet we got in this direction? */
625         if (statinfo->flags & STAT_FLAG_FIRST) {
626                 add_to_list(list, user_data,
627                         pinfo->fd->num, rtpinfo->info_seq_num,
628                         statinfo->timestamp,
629                         0,
630                         0,
631                         0,
632                         statinfo->bandwidth,
633                         status,
634                         rtpinfo->info_marker_set,
635                         timeStr, pinfo->fd->pkt_len,
636                         color_str,
637                         statinfo->flags);
638         }
639         else {
640                 add_to_list(list, user_data,
641                         pinfo->fd->num, rtpinfo->info_seq_num,
642                         statinfo->timestamp,
643                         statinfo->delta,
644                         statinfo->jitter,
645                         statinfo->skew,
646                         statinfo->bandwidth,
647                         status,
648                         rtpinfo->info_marker_set,
649                         timeStr, pinfo->fd->pkt_len,
650                         color_str,
651                         statinfo->flags);
652         }
653         return 0;
654 }
655
656 #define MAX_SILENCE_TICKS 1000000
657 /****************************************************************************/
658 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
659                                    tap_rtp_stat_t *statinfo,
660                                    packet_info *pinfo,
661                                    const struct _rtp_info *rtpinfo)
662 {
663         guint i;
664         const guint8 *data;
665         guint8 tmp;
666         size_t nchars;
667
668         /*  is this the first packet we got in this direction? */
669         if (statinfo->flags & STAT_FLAG_FIRST) {
670                 if (saveinfo->fp == NULL) {
671                         saveinfo->saved = FALSE;
672                         saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
673                 }
674                 else
675                         saveinfo->saved = TRUE;
676         }
677
678         /* save the voice information */
679         /* if there was already an error, we quit */
680         if (saveinfo->saved == FALSE)
681                 return 0;
682
683         /* if the captured length and packet length aren't equal, we quit
684         * if also the RTP dissector thinks there is some information missing */
685         if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
686             (!rtpinfo->info_all_data_present)) {
687                 saveinfo->saved = FALSE;
688                 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
689                 return 0;
690         }
691
692         /* if padding bit is set, but the padding count is bigger
693         * then the whole RTP data - error with padding count */
694         if ( (rtpinfo->info_padding_set != FALSE) &&
695                 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
696                 saveinfo->saved = FALSE;
697                 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
698                 return 0;
699         }
700
701         /* do we need to insert some silence? */
702         if ((rtpinfo->info_marker_set) &&
703                 !(statinfo->flags & STAT_FLAG_FIRST) &&
704                 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
705                 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) )  {
706                 /* the amount of silence should be the difference between
707                 * the last timestamp and the current one minus x
708                 * x should equal the amount of information in the last frame
709                 * XXX not done yet */
710                 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
711                         rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
712                         switch (statinfo->reg_pt) {
713                         case PT_PCMU:
714                                 tmp = SILENCE_PCMU;
715                                 break;
716                         case PT_PCMA:
717                                 tmp = SILENCE_PCMA;
718                                 break;
719                         default:
720                                 tmp = 0;
721                                 break;
722                         }
723                         nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
724                         if (nchars != 1) {
725                                 /* Write error or short write */
726                                 saveinfo->saved = FALSE;
727                                 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
728                                 return 0;
729                         }
730                         saveinfo->count++;
731                 }
732                 fflush(saveinfo->fp);
733         }
734
735
736         if (rtpinfo->info_payload_type == PT_CN
737                 || rtpinfo->info_payload_type == PT_CN_OLD) {
738         }
739         /*all other payloads*/
740         else {
741                 if (!rtpinfo->info_all_data_present) {
742                         /* Not all the data was captured. */
743                         saveinfo->saved = FALSE;
744                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
745                         return 0;
746                 }
747
748                 /* we put the pointer at the beginning of the RTP
749                 * payload, that is, at the beginning of the RTP data
750                 * plus the offset of the payload from the beginning
751                 * of the RTP data */
752                 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
753                 nchars = fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
754                 if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
755                         /* Write error or short write */
756                         saveinfo->saved = FALSE;
757                         saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
758                         return 0;
759                 }
760                 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
761
762                 fflush(saveinfo->fp);
763                 saveinfo->saved = TRUE;
764                 return 0;
765         }
766
767         return 0;
768 }
769
770
771 /****************************************************************************/
772 /* CALLBACKS */
773
774 /****************************************************************************/
775
776 /****************************************************************************/
777 /* close the dialog window and remove the tap listener */
778 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
779 {
780         /* remove tap listener */
781         protect_thread_critical_region();
782         remove_tap_listener(user_data);
783         unprotect_thread_critical_region();
784
785         /* close and remove temporary files */
786         if (user_data->forward.saveinfo.fp != NULL)
787                 fclose(user_data->forward.saveinfo.fp);
788         if (user_data->reversed.saveinfo.fp != NULL)
789                 fclose(user_data->reversed.saveinfo.fp);
790         /*XXX: test for error **/
791         ws_remove(user_data->f_tempname);
792         ws_remove(user_data->r_tempname);
793
794 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
795         /* destroy save_voice_as window if open */
796         if (user_data->dlg.save_voice_as_w != NULL)
797                 window_destroy(user_data->dlg.save_voice_as_w);
798 #endif
799         /* destroy graph window if open */
800         if (user_data->dlg.dialog_graph.window != NULL)
801                 window_destroy(user_data->dlg.dialog_graph.window);
802
803         /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
804         g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
805
806         g_free(user_data->f_tempname);
807         g_free(user_data->r_tempname);
808         g_free(user_data);
809 }
810
811
812 /****************************************************************************/
813 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
814                                     gpointer *page _U_,
815                                     gint page_num _U_,
816                                     user_data_t *user_data _U_)
817 {
818         user_data->dlg.selected_list =
819                 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
820
821         user_data->dlg.selected_list_row = 0;
822 }
823
824 /****************************************************************************/
825
826 static void on_list_select_row(GtkTreeSelection *selection,
827                                                            user_data_t *user_data/*gpointer data */)
828 {
829         user_data->dlg.selected_list_sel = selection;
830 }
831
832
833 /****************************************************************************/
834 static void dialog_graph_set_title(user_data_t* user_data)
835 {
836         char            *title;
837         if (!user_data->dlg.dialog_graph.window){
838                 return;
839         }
840         title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u   Reverse: %s:%u to %s:%u",
841                         get_addr_name(&(user_data->ip_src_fwd)),
842                         user_data->port_src_fwd,
843                         get_addr_name(&(user_data->ip_dst_fwd)),
844                         user_data->port_dst_fwd,
845                         get_addr_name(&(user_data->ip_src_rev)),
846                         user_data->port_src_rev,
847                         get_addr_name(&(user_data->ip_dst_rev)),
848                         user_data->port_dst_rev);
849
850         gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
851         g_free(title);
852
853 }
854
855
856 /****************************************************************************/
857 static void dialog_graph_reset(user_data_t* user_data)
858 {
859         int i, j;
860
861         user_data->dlg.dialog_graph.needs_redraw=TRUE;
862         for(i=0;i<MAX_GRAPHS;i++){
863                 for(j=0;j<NUM_GRAPH_ITEMS;j++){
864                         dialog_graph_graph_item_t *dggi;
865                         dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
866                         dggi->value=0;
867                         dggi->flags=0;
868                 }
869         }
870         user_data->dlg.dialog_graph.last_interval=0xffffffff;
871         user_data->dlg.dialog_graph.max_interval=0;
872         user_data->dlg.dialog_graph.num_items=0;
873
874         /* create the color titles near the filter buttons */
875         for(i=0;i<MAX_GRAPHS;i++){
876                 /* it is forward */
877                 if (i<(MAX_GRAPHS/2)){
878                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
879                                    sizeof(user_data->dlg.dialog_graph.graph[0].title),
880                                    "%s: %s:%u to %s:%u (SSRC=0x%X)",
881                                    graph_descr[i],
882                                    get_addr_name(&(user_data->ip_src_fwd)),
883                                    user_data->port_src_fwd,
884                                    get_addr_name(&(user_data->ip_dst_fwd)),
885                                    user_data->port_dst_fwd,
886                                    user_data->ssrc_fwd);
887                 /* it is reverse */
888                 } else {
889                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
890                                    sizeof(user_data->dlg.dialog_graph.graph[0].title),
891                                    "%s: %s:%u to %s:%u (SSRC=0x%X)",
892                                    graph_descr[i],
893                                    get_addr_name(&(user_data->ip_src_rev)),
894                                    user_data->port_src_rev,
895                                    get_addr_name(&(user_data->ip_dst_rev)),
896                                    user_data->port_dst_rev,
897                                    user_data->ssrc_rev);
898                 }
899         }
900
901         dialog_graph_set_title(user_data);
902 }
903
904 /****************************************************************************/
905 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
906 {
907         dialog_graph_graph_item_t *it;
908
909         it=&dgg->items[idx];
910
911         return it->value;
912 }
913
914 /****************************************************************************/
915 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
916 {
917         if(t>=10000000){
918                 g_snprintf(buf, buf_len, "%ds",t/1000000);
919         } else if(t>=1000000){
920                 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
921         } else if(t>=10000){
922                 g_snprintf(buf, buf_len, "%dms",t/1000);
923         } else if(t>=1000){
924                 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
925         } else {
926                 g_snprintf(buf, buf_len, "%dus",t);
927         }
928 }
929
930 /****************************************************************************/
931 static void dialog_graph_draw(user_data_t* user_data)
932 {
933         int i, lwidth;
934         guint32 last_interval, first_interval, interval_delta, delta_multiplier;
935         gint32 current_interval;
936         guint32 left_x_border;
937         guint32 right_x_border;
938         guint32 top_y_border;
939         guint32 bottom_y_border;
940         PangoLayout  *layout;
941         int label_width, label_height;
942         int label_width_mid, label_height_mid;
943         guint32 draw_width, draw_height;
944         char label_string[15];
945         GtkAllocation widget_alloc;
946         cairo_t *cr;
947
948         /* new variables */
949         guint32 num_time_intervals;
950         guint32 max_value;              /* max value of seen data */
951         guint32 max_y;                  /* max value of the Y scale */
952
953         if(!user_data->dlg.dialog_graph.needs_redraw){
954                 return;
955         }
956         user_data->dlg.dialog_graph.needs_redraw=FALSE;
957
958         /*
959          * Find the length of the intervals we have data for
960          * so we know how large arrays we need to malloc()
961          */
962         num_time_intervals=user_data->dlg.dialog_graph.num_items;
963         /* if there isnt anything to do, just return */
964         if(num_time_intervals==0){
965                 return;
966         }
967         num_time_intervals+=1;
968         /* XXX move this check to _packet() */
969         if(num_time_intervals>NUM_GRAPH_ITEMS){
970                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
971                 return;
972         }
973
974         /*
975          * find the max value so we can autoscale the y axis
976          */
977         max_value=0;
978         for(i=0;i<MAX_GRAPHS;i++){
979                 int idx;
980
981                 if(!user_data->dlg.dialog_graph.graph[i].display){
982                         continue;
983                 }
984                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
985                         guint32 val;
986
987                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
988
989                         /* keep track of the max value we have encountered */
990                         if(val>max_value){
991                                 max_value=val;
992                         }
993                 }
994         }
995
996         /*
997          * Clear out old plot
998          */
999 #if GTK_CHECK_VERSION(2,22,0)
1000         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1001 #else
1002         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1003 #endif
1004         cairo_set_source_rgb (cr, 1, 1, 1);
1005         gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
1006         cairo_rectangle (cr,
1007                 0,
1008                 0,
1009                 widget_alloc.width,
1010                 widget_alloc.height);
1011         cairo_fill (cr);
1012         cairo_destroy (cr);
1013
1014         /*
1015          * Calculate the y scale we should use
1016          */
1017         if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1018                 max_y=yscale_max[MAX_YSCALE-1];
1019                 for(i=MAX_YSCALE-1;i>0;i--){
1020                         if(max_value<yscale_max[i]){
1021                                 max_y=yscale_max[i];
1022                         }
1023                 }
1024         } else {
1025                 /* the user had specified an explicit y scale to use */
1026                 max_y=user_data->dlg.dialog_graph.max_y_units;
1027         }
1028
1029         /*
1030          * Calculate size of borders surrounding the plot
1031          * The border on the right side needs to be adjusted depending
1032          * on the width of the text labels.
1033          */
1034         print_time_scale_string(label_string, sizeof(label_string), max_y);
1035         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1036         pango_layout_get_pixel_size(layout, &label_width, &label_height);
1037         print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1038         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1039         pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1040         if (label_width_mid > label_width) {
1041                 label_width = label_width_mid;
1042                 label_height = label_height_mid;
1043         }
1044
1045         left_x_border=10;
1046         right_x_border=label_width+20;
1047         top_y_border=10;
1048         bottom_y_border=label_height+20;
1049
1050
1051         /*
1052          * Calculate the size of the drawing area for the actual plot
1053          */
1054         draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
1055         draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
1056
1057
1058         /*
1059          * Draw the y axis and labels
1060          * (we always draw the y scale with 11 ticks along the axis)
1061          */
1062 #if GTK_CHECK_VERSION(2,22,0)
1063         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1064 #else
1065         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1066 #endif
1067         cairo_set_line_width (cr, 1.0);
1068         cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
1069         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);
1070         cairo_stroke(cr);
1071         cairo_destroy(cr);
1072
1073         for(i=0;i<=10;i++){
1074                 int xwidth;
1075
1076                 xwidth=5;
1077                 if(!(i%5)){
1078                         /* first, middle and last tick are slightly longer */
1079                         xwidth=10;
1080                 }
1081                 /* draw the tick */
1082 #if GTK_CHECK_VERSION(2,22,0)
1083                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1084 #else
1085                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1086 #endif
1087                 cairo_set_line_width (cr, 1.0);
1088                 cairo_move_to(cr,
1089                         user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,
1090                         user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1091                 
1092                 cairo_line_to(cr,
1093                         user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
1094                         user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1095                 cairo_stroke(cr);
1096                 cairo_destroy(cr);
1097                 /* draw the labels */
1098                 if(i==0){
1099                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1100                         pango_layout_set_text(layout, label_string, -1);
1101                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1102 #if GTK_CHECK_VERSION(2,22,0)
1103                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1104 #else
1105                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1106 #endif
1107                         cairo_move_to (cr,
1108                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1109                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1110                         pango_cairo_show_layout (cr, layout);
1111                         cairo_destroy (cr);
1112                         cr = NULL;
1113                 }
1114                 if(i==5){
1115                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1116                         pango_layout_set_text(layout, label_string, -1);
1117                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1118 #if GTK_CHECK_VERSION(2,22,0)
1119                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1120 #else
1121                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1122 #endif
1123                         cairo_move_to (cr,
1124                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1125                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1126                         pango_cairo_show_layout (cr, layout);
1127                         cairo_destroy (cr);
1128                         cr = NULL;
1129                 }
1130                 if(i==10){
1131                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1132                         pango_layout_set_text(layout, label_string, -1);
1133                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1134 #if GTK_CHECK_VERSION(2,22,0)
1135                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1136 #else
1137                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1138 #endif
1139                         cairo_move_to (cr,
1140                                 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1141                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1142                         pango_cairo_show_layout (cr, layout);
1143                         cairo_destroy (cr);
1144                         cr = NULL;
1145                 }
1146         }
1147
1148
1149
1150         /*
1151          * if we have not specified the last_interval via the gui,
1152          * then just pick the current end of the capture so that is scrolls
1153          * nicely when doing live captures
1154          */
1155         if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1156                 last_interval=user_data->dlg.dialog_graph.max_interval;
1157         } else {
1158                 last_interval=user_data->dlg.dialog_graph.last_interval;
1159         }
1160
1161
1162
1163
1164 /*XXX*/
1165         /* plot the x-scale */
1166 #if GTK_CHECK_VERSION(2,22,0)
1167         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1168 #else
1169         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1170 #endif
1171         cairo_set_line_width (cr, 1.0);
1172         cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1173         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);
1174         cairo_stroke(cr);
1175         cairo_destroy(cr);
1176
1177         if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1178                 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1179                 first_interval*=user_data->dlg.dialog_graph.interval;
1180         } else {
1181                 first_interval=0;
1182         }
1183
1184         interval_delta=1;
1185         delta_multiplier=5;
1186         while(interval_delta<((last_interval-first_interval)/10)){
1187                 interval_delta*=delta_multiplier;
1188                 if(delta_multiplier==5){
1189                         delta_multiplier=2;
1190                 } else {
1191                         delta_multiplier=5;
1192                 }
1193         }
1194
1195         for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1196                 int x, xlen;
1197
1198                 /* if pixels_per_tick is <5, only draw every 10 ticks */
1199                 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1200                         continue;
1201                 }
1202
1203                 if(current_interval%interval_delta){
1204                         xlen=5;
1205                 } else {
1206                         xlen=17;
1207                 }
1208
1209                 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1210 #if GTK_CHECK_VERSION(2,22,0)
1211                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1212 #else
1213                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1214 #endif
1215                 cairo_set_line_width (cr, 1.0);
1216                 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);
1217                 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);
1218                 cairo_stroke(cr);
1219                 cairo_destroy(cr);
1220
1221                 if(xlen==17){
1222                         if(user_data->dlg.dialog_graph.interval>=1000){
1223                                 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1224                         } else if(user_data->dlg.dialog_graph.interval>=100){
1225                                 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1226                         } else if(user_data->dlg.dialog_graph.interval>=10){
1227                                 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1228                         } else {
1229                                 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1230                         }
1231                         pango_layout_set_text(layout, label_string, -1);
1232                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1233 #if GTK_CHECK_VERSION(2,22,0)
1234                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1235 #else
1236                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1237 #endif
1238                         cairo_move_to (cr,
1239                                 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1240                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
1241                         pango_cairo_show_layout (cr, layout);
1242                         cairo_destroy (cr);
1243                         cr = NULL;
1244                 }
1245
1246         }
1247
1248
1249
1250
1251
1252
1253         /*
1254          * Draw "x" for Sequence Errors and "m" for Marks
1255          */
1256         /* Draw the labels Fwd and Rev */
1257         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
1258         pango_layout_set_text(layout, label_string, -1);
1259         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1260 #if GTK_CHECK_VERSION(2,22,0)
1261         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1262 #else
1263         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1264 #endif
1265         cairo_move_to (cr,
1266                 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1267                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
1268         pango_cairo_show_layout (cr, layout);
1269         cairo_destroy (cr);
1270         cr = NULL;
1271
1272         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
1273         pango_layout_set_text(layout, label_string, -1);
1274         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1275 #if GTK_CHECK_VERSION(2,22,0)
1276         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1277 #else
1278         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1279 #endif
1280         cairo_move_to (cr,
1281                 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1282                 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
1283         pango_cairo_show_layout (cr, layout);
1284         cairo_destroy (cr);
1285         cr = NULL;
1286
1287         /* Draw the marks */
1288         for(i=MAX_GRAPHS-1;i>=0;i--){
1289                 guint32 interval;
1290                 guint32 x_pos/*, prev_x_pos*/;
1291
1292                 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1293                 if (!user_data->dlg.dialog_graph.graph[i].display){
1294                         continue;
1295                 }
1296                 /* initialize prev x/y to the low left corner of the graph */
1297                 /*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;*/
1298
1299                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1300                         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;
1301
1302                         if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1303                                 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1304                                         g_strlcpy(label_string,"x",sizeof(label_string));
1305                                 } else {
1306                                         g_strlcpy(label_string,"m",sizeof(label_string));
1307                                 }
1308
1309                                 pango_layout_set_text(layout, label_string, -1);
1310                                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1311 #if GTK_CHECK_VERSION(2,22,0)
1312                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1313 #else
1314                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1315 #endif
1316                                 cairo_move_to (cr,
1317                                         x_pos-1-lwidth/2,
1318                                         user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
1319                                 pango_cairo_show_layout (cr, layout);
1320                                 cairo_destroy (cr);
1321                                 cr = NULL;
1322
1323                         }
1324
1325                         /*prev_x_pos=x_pos;*/
1326                 }
1327         }
1328
1329         g_object_unref(G_OBJECT(layout));
1330
1331         /*
1332          * Loop over all graphs and draw them
1333          */
1334         for(i=MAX_GRAPHS-1;i>=0;i--){
1335                 guint32 interval;
1336                 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1337                 if (!user_data->dlg.dialog_graph.graph[i].display){
1338                         continue;
1339                 }
1340                 /* initialize prev x/y to the low left corner of the graph */
1341                 /*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;*/
1342                 prev_y_pos=draw_height-1+top_y_border;
1343
1344                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1345                         guint32 val;
1346                         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;
1347                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1348                         if(val>max_y){
1349                                 y_pos=0;
1350                         } else {
1351                                 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1352                         }
1353
1354                         /* dont need to draw anything if the segment
1355                          * is entirely above the top of the graph
1356                          */
1357                         if( (prev_y_pos==0) && (y_pos==0) ){
1358                                 prev_y_pos=y_pos;
1359                                 /*prev_x_pos=x_pos;*/
1360                                 continue;
1361                         }
1362
1363                         if(val){
1364 #if GTK_CHECK_VERSION(2,22,0)
1365                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1366 #else
1367                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1368 #endif
1369                                 gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
1370                                 cairo_set_line_width (cr, 1.0);
1371                                 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1372                                 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1373                                 cairo_stroke(cr);
1374                                 cairo_destroy(cr);
1375                         }
1376
1377                         prev_y_pos=y_pos;
1378                         /*prev_x_pos=x_pos;*/
1379                 }
1380         }
1381
1382         cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1383
1384 #if GTK_CHECK_VERSION(2,22,0)
1385         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1386 #else
1387         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1388 #endif
1389         cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1390         cairo_fill (cr);
1391
1392         cairo_destroy (cr);
1393
1394         /* update the scrollbar */
1395         gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
1396         gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1397         gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1398         if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1399                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1400         } else {
1401                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1402         }
1403         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));
1404         gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1405         gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1406
1407 }
1408
1409 /****************************************************************************/
1410 static void dialog_graph_redraw(user_data_t* user_data)
1411 {
1412         user_data->dlg.dialog_graph.needs_redraw=TRUE;
1413         dialog_graph_draw(user_data);
1414 }
1415
1416 /****************************************************************************/
1417 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1418 {
1419         GtkWidget *bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1420         surface_info_t *surface_info = g_object_get_data(G_OBJECT(bt_save), "surface-info");
1421
1422         g_free(surface_info);
1423         user_data->dlg.dialog_graph.window = NULL;
1424 }
1425
1426 /****************************************************************************/
1427 #if GTK_CHECK_VERSION(3,0,0)
1428 static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
1429 {
1430         user_data_t *user_data = data;
1431         GtkAllocation allocation;
1432
1433         gtk_widget_get_allocation (widget, &allocation);
1434
1435         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1436         cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
1437         cairo_fill (cr);
1438
1439         return FALSE;
1440 }
1441 #else
1442 static gint expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1443 {
1444         user_data_t *user_data = data;
1445         cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1446
1447
1448 #if GTK_CHECK_VERSION(2,22,0)
1449         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1450 #else
1451         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1452 #endif
1453         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1454         cairo_fill (cr);
1455
1456         cairo_destroy (cr);
1457
1458         return FALSE;
1459 }
1460 #endif
1461 /****************************************************************************/
1462 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1463 {
1464         user_data_t *user_data;
1465         GtkWidget *bt_save;
1466         GtkAllocation widget_alloc;
1467         cairo_t *cr;
1468 #if GTK_CHECK_VERSION(2,22,0)
1469         surface_info_t *surface_info = g_new(surface_info_t, 1);
1470 #endif
1471
1472         user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1473
1474         if(!user_data){
1475                 exit(10);
1476         }
1477
1478 #if GTK_CHECK_VERSION(2,22,0)
1479         if(user_data->dlg.dialog_graph.surface){
1480                 g_object_unref(user_data->dlg.dialog_graph.surface);
1481                 user_data->dlg.dialog_graph.surface=NULL;
1482         }
1483         gtk_widget_get_allocation(widget, &widget_alloc);
1484         user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1485                         CAIRO_CONTENT_COLOR,
1486                         widget_alloc.width,
1487                         widget_alloc.height);
1488 #else
1489         if(user_data->dlg.dialog_graph.pixmap){
1490                 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1491                 user_data->dlg.dialog_graph.pixmap=NULL;
1492         }
1493
1494         gtk_widget_get_allocation(widget, &widget_alloc);
1495         user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1496                                                           widget_alloc.width,
1497                                                           widget_alloc.height,
1498                                                           -1);
1499 #endif
1500         user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
1501         user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
1502
1503         bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1504 #if GTK_CHECK_VERSION(2,22,0)
1505         surface_info->surface = user_data->dlg.dialog_graph.surface;
1506         surface_info->width = widget_alloc.width;
1507         surface_info->height = widget_alloc.height;
1508         g_object_set_data(G_OBJECT(bt_save), "surface-info", surface_info);
1509         gtk_widget_set_sensitive(bt_save, TRUE);
1510
1511         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1512 #else
1513         g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1514         gtk_widget_set_sensitive(bt_save, TRUE);
1515
1516         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1517 #endif
1518         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1519         cairo_set_source_rgb (cr, 1, 1, 1);
1520         cairo_fill (cr);
1521         cairo_destroy (cr);
1522
1523         dialog_graph_redraw(user_data);
1524         return TRUE;
1525 }
1526
1527 /****************************************************************************/
1528 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1529 {
1530         user_data_t *user_data=(user_data_t *)data;
1531         guint32 mi;
1532
1533         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));
1534         if(user_data->dlg.dialog_graph.last_interval==mi){
1535                 return TRUE;
1536         }
1537         if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1538             &&  (mi==user_data->dlg.dialog_graph.max_interval) ){
1539                 return TRUE;
1540         }
1541
1542         user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1543
1544         dialog_graph_redraw(user_data);
1545         return TRUE;
1546 }
1547
1548 /****************************************************************************/
1549 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1550 {
1551         user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1552         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1553
1554         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);
1555
1556         /* signals needed to handle backing pixmap */
1557 #if GTK_CHECK_VERSION(3,0,0)
1558         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
1559 #else
1560         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
1561 #endif
1562         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1563
1564         gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1565         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1566
1567         /* create the associated scrollbar */
1568         user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1569         user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1570         gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1571         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1572         g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1573 }
1574
1575 /****************************************************************************/
1576 static void disable_graph(dialog_graph_graph_t *dgg)
1577 {
1578         if (dgg->display) {
1579                 dgg->display=FALSE;
1580                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1581                                              FALSE);
1582         }
1583 }
1584
1585 /****************************************************************************/
1586 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1587 {
1588         /* this graph is not active, just update display and redraw */
1589         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1590                 disable_graph(dgg);
1591                 dialog_graph_redraw(dgg->ud);
1592                 return 0;
1593         }
1594
1595         enable_graph(dgg);
1596         cf_retap_packets(&cfile);
1597         dialog_graph_redraw(dgg->ud);
1598
1599         return 0;
1600 }
1601
1602 /****************************************************************************/
1603 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1604 {
1605         GtkWidget *hbox;
1606         GtkWidget *label;
1607         char str[256];
1608
1609         hbox=gtk_hbox_new(FALSE, 3);
1610         gtk_container_add(GTK_CONTAINER(box), hbox);
1611         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1612         gtk_widget_show(hbox);
1613
1614         g_snprintf(str, sizeof(str), "Graph %d", num);
1615         dgg->display_button=gtk_toggle_button_new_with_label(str);
1616         gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1617         gtk_widget_show(dgg->display_button);
1618         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1619         g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1620
1621         label=gtk_label_new(dgg->title);
1622         gtk_widget_show(label);
1623         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1624
1625 #if GTK_CHECK_VERSION(3,0,0)
1626         gtk_widget_override_color(label, GTK_STATE_NORMAL, &dgg->rgba_color);
1627         gtk_widget_override_color(label, GTK_STATE_ACTIVE, &dgg->rgba_color);
1628         gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &dgg->rgba_color);
1629         gtk_widget_override_color(label, GTK_STATE_SELECTED, &dgg->rgba_color);
1630         gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &dgg->rgba_color);
1631 #else   
1632         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1633         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1634         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1635         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1636         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1637 #endif
1638         return;
1639 }
1640
1641 /****************************************************************************/
1642 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1643 {
1644         GtkWidget *frame;
1645         GtkWidget *vbox;
1646         int i;
1647         GtkWidget *label;
1648
1649         frame=gtk_frame_new("Graphs");
1650         gtk_container_add(GTK_CONTAINER(box), frame);
1651         gtk_widget_show(frame);
1652
1653         vbox=gtk_vbox_new(FALSE, 1);
1654         gtk_container_add(GTK_CONTAINER(frame), vbox);
1655         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1656         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1657         gtk_widget_show(vbox);
1658
1659         for(i=0;i<MAX_GRAPHS;i++){
1660                 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1661         }
1662
1663         label=gtk_label_new("Label:    x = Wrong Seq. number      m = Mark set");
1664         gtk_widget_show(label);
1665         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1666
1667         return;
1668 }
1669
1670 /****************************************************************************/
1671 static void yscale_select(GtkWidget *item, gpointer key)
1672 {
1673         int i;
1674         user_data_t *user_data;
1675
1676         user_data=(user_data_t *)key;
1677         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1678
1679         user_data->dlg.dialog_graph.max_y_units_index=i;
1680         user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1681         dialog_graph_redraw(user_data);
1682 }
1683
1684 /****************************************************************************/
1685 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1686 {
1687         int i;
1688         user_data_t *user_data;
1689
1690         user_data=(user_data_t *)key;
1691         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1692
1693         user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1694         user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1695         dialog_graph_redraw(user_data);
1696 }
1697
1698 /****************************************************************************/
1699 static void tick_interval_select(GtkWidget *item, gpointer key)
1700 {
1701         int i;
1702         user_data_t *user_data;
1703
1704         user_data=(user_data_t *)key;
1705         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1706
1707         user_data->dlg.dialog_graph.interval_index=i;
1708         user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1709         cf_retap_packets(&cfile);
1710         dialog_graph_redraw(user_data);
1711 }
1712
1713 /****************************************************************************/
1714 static GtkWidget *
1715 create_yscale_max_menu_items(user_data_t* user_data)
1716 {
1717         char str[15];
1718         GtkWidget *combo_box;
1719         int i;
1720
1721         combo_box = gtk_combo_box_text_new();
1722
1723         for(i=0;i<MAX_YSCALE;i++){
1724                 if(yscale_max[i]==AUTO_MAX_YSCALE){
1725                         g_strlcpy(str,"Auto",sizeof(str));
1726                 } else if (yscale_max[i] < 1000000) {
1727                         g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1728                 } else {
1729                         g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1730                 }
1731                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1732         }
1733         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1734         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1735
1736         return combo_box;
1737 }
1738
1739 /****************************************************************************/
1740 static GtkWidget *
1741 create_pixels_per_tick_menu_items(user_data_t *user_data)
1742 {
1743         char str[5];
1744         GtkWidget *combo_box;
1745         int i;
1746
1747         combo_box = gtk_combo_box_text_new();
1748
1749         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1750                 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1751                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1752         }
1753         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1754
1755         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1756
1757         return combo_box;
1758 }
1759
1760 /****************************************************************************/
1761 static GtkWidget *
1762 create_tick_interval_menu_items(user_data_t *user_data)
1763 {
1764         GtkWidget *combo_box;
1765         char str[15];
1766         int i;
1767
1768         combo_box = gtk_combo_box_text_new();
1769
1770         for(i=0;i<MAX_TICK_VALUES;i++){
1771                 if(tick_interval_values[i]>=1000){
1772                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1773                 } else if(tick_interval_values[i]>=100){
1774                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1775                 } else if(tick_interval_values[i]>=10){
1776                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1777                 } else {
1778                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1779                 }
1780                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1781         }
1782         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1783         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1784
1785         return combo_box;
1786 }
1787
1788 /****************************************************************************/
1789 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1790 {
1791         GtkWidget *hbox;
1792         GtkWidget *label;
1793         GtkWidget *combo_box;
1794
1795         hbox=gtk_hbox_new(FALSE, 0);
1796         gtk_container_add(GTK_CONTAINER(box), hbox);
1797         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1798         gtk_widget_show(hbox);
1799
1800         label=gtk_label_new(name);
1801         gtk_widget_show(label);
1802         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1803
1804         combo_box = (*func)(user_data);
1805         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1806         gtk_widget_show(combo_box);
1807 }
1808
1809 /****************************************************************************/
1810 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1811 {
1812         GtkWidget *frame_vbox;
1813         GtkWidget *frame;
1814         GtkWidget *vbox;
1815
1816         frame_vbox=gtk_vbox_new(FALSE, 0);
1817         gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1818         gtk_widget_show(frame_vbox);
1819
1820         frame = gtk_frame_new("X Axis");
1821         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1822         gtk_widget_show(frame);
1823
1824         vbox=gtk_vbox_new(FALSE, 0);
1825         gtk_container_add(GTK_CONTAINER(frame), vbox);
1826         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1827         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1828         gtk_widget_show(vbox);
1829
1830         create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1831         create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1832
1833         frame = gtk_frame_new("Y Axis");
1834         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1835         gtk_widget_show(frame);
1836
1837         vbox=gtk_vbox_new(FALSE, 0);
1838         gtk_container_add(GTK_CONTAINER(frame), vbox);
1839         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1840         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1841         gtk_widget_show(vbox);
1842
1843         create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1844
1845         return;
1846 }
1847
1848 /****************************************************************************/
1849 static void dialog_graph_init_window(user_data_t* user_data)
1850 {
1851         GtkWidget *vbox;
1852         GtkWidget *hbox;
1853         GtkWidget *bt_close;
1854         GtkWidget *bt_save;
1855
1856         /* create the main window */
1857         user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs");   /* transient_for top_level */
1858
1859         vbox=gtk_vbox_new(FALSE, 0);
1860         gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1861         gtk_widget_show(vbox);
1862
1863         create_draw_area(user_data, vbox);
1864
1865         hbox=gtk_hbox_new(FALSE, 3);
1866         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1867         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1868         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1869         gtk_widget_show(hbox);
1870
1871         create_filter_area(user_data, hbox);
1872         create_ctrl_area(user_data, hbox);
1873
1874         dialog_graph_set_title(user_data);
1875
1876         hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1877         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1878         gtk_widget_show(hbox);
1879
1880         bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1881         window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1882
1883         bt_save = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1884         gtk_widget_set_sensitive(bt_save, FALSE);
1885         gtk_widget_set_tooltip_text(bt_save, "Save the displayed graph to a file");
1886         g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1887         g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1888
1889         g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1890
1891         gtk_widget_show(user_data->dlg.dialog_graph.window);
1892         window_present(user_data->dlg.dialog_graph.window);
1893
1894 }
1895
1896
1897 /****************************************************************************/
1898 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1899 {
1900         if (user_data->dlg.dialog_graph.window != NULL) {
1901                 /* There's already a graph window; reactivate it. */
1902                 reactivate_window(user_data->dlg.dialog_graph.window);
1903                 return;
1904         }
1905
1906         dialog_graph_init_window(user_data);
1907
1908 }
1909
1910 /****************************************************************************/
1911
1912 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1913 {
1914         GtkTreeIter iter;
1915         GtkTreeModel *model;
1916         GtkTreeSelection *selection;
1917         guint fnumber;
1918
1919         selection = user_data->dlg.selected_list_sel;
1920
1921         if (selection==NULL)
1922                 return;
1923
1924         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1925                 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1926                 cf_goto_frame(&cfile, fnumber);
1927         }
1928
1929 }
1930
1931 static void draw_stat(user_data_t *user_data);
1932
1933 /****************************************************************************/
1934 /* re-dissects all packets */
1935 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1936 {
1937         GString *error_string;
1938
1939         /* remove tap listener */
1940         protect_thread_critical_region();
1941         remove_tap_listener(user_data);
1942         unprotect_thread_critical_region();
1943
1944         /* register tap listener */
1945         error_string = register_tap_listener("rtp", user_data, NULL, 0,
1946                 rtp_reset, rtp_packet, rtp_draw);
1947         if (error_string != NULL) {
1948                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1949                         g_string_free(error_string, TRUE);
1950                 return;
1951         }
1952
1953         /* retap all packets */
1954         cf_retap_packets(&cfile);
1955
1956         /* draw statistics info */
1957         draw_stat(user_data);
1958
1959 }
1960
1961 #ifdef HAVE_LIBPORTAUDIO
1962 /****************************************************************************/
1963 static void
1964 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
1965 {
1966         /*rtp_player_init(voip_calls_get_info());*/
1967         rtp_player_init(NULL);
1968 }
1969 #endif /* HAVE_LIBPORTAUDIO */
1970
1971 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1972 {
1973         GtkTreeIter iter;
1974         GtkTreeModel *model;
1975         gchar *text;
1976         GtkTreeSelection *selection;
1977         GtkTreePath *path;
1978
1979         selection = user_data->dlg.selected_list_sel;
1980
1981         if (selection==NULL)
1982                 return;
1983
1984 try_again:
1985         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1986                 while (gtk_tree_model_iter_next (model,&iter)) {
1987                         gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1988                         if (strcmp(text, OK_TEXT) != 0) {
1989                                 gtk_tree_selection_select_iter (selection, &iter);
1990                                 path = gtk_tree_model_get_path(model, &iter);
1991                                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1992                                                 path,
1993                                                 NULL, FALSE, 0, 0);
1994                                 gtk_tree_path_free(path);
1995                                 g_free (text);
1996                                 return;
1997                         }
1998                         g_free (text);
1999                 }
2000                 /* wrap around */
2001                 if (user_data->dlg.number_of_nok>1){
2002                         /* Get the first iter and select it before starting over */
2003                         gtk_tree_model_get_iter_first(model, &iter);
2004                         gtk_tree_selection_select_iter (selection, &iter);
2005                         goto try_again;
2006                 }
2007         }
2008 }
2009
2010
2011 /****************************************************************************/
2012 /* when we want to save the information */
2013 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
2014 {
2015         gchar *g_dest;
2016         GtkWidget *rev, *forw, *both;
2017         user_data_t *user_data;
2018
2019         GtkListStore *store;
2020         GtkTreeIter iter;
2021         GtkTreeModel *model;
2022         gboolean more_items = TRUE;
2023
2024         /* To Hold data from the list row */
2025         guint32                 packet;         /* Packet                       */
2026         guint16                 sequence;       /* Sequence                     */
2027         guint32                 timestamp;      /* timestamp                    */
2028         gfloat                  delta;          /* Delta(ms)                    */
2029         gfloat                  jitter;         /* Jitter(ms)                   */
2030         gfloat                  skew;           /* Skew(ms)                     */
2031         gfloat                  ipbw;           /* IP BW(kbps)                  */
2032         gboolean                marker;         /* Marker                       */
2033         char *                  status_str;     /* Status                       */
2034         char *                  date_str;       /* Date                         */
2035         guint                   length;         /* Length                       */
2036
2037
2038         FILE *fp;
2039         int j;
2040
2041         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2042
2043         /* Perhaps the user specified a directory instead of a file.
2044          * Check whether they did.
2045          */
2046         if (test_for_directory(g_dest) == EISDIR) {
2047                 /* It's a directory - set the file selection box to display it. */
2048                 set_last_open_dir(g_dest);
2049                 g_free(g_dest);
2050                 file_selection_set_current_folder(fc, get_last_open_dir());
2051                 gtk_file_chooser_set_current_name(fc, "");
2052                 return FALSE; /* run the dialog again */
2053         }
2054
2055         rev  = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2056         forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
2057         both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
2058         user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
2059
2060         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2061                 fp = ws_fopen(g_dest, "w");
2062                 if (fp == NULL) {
2063                         open_failure_alert_box(g_dest, errno, TRUE);
2064                         g_free(g_dest);
2065                         return TRUE; /* we're done */
2066                 }
2067
2068                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2069                         fprintf(fp, "Forward\n");
2070                         if (ferror(fp)) {
2071                                 write_failure_alert_box(g_dest, errno);
2072                                 fclose(fp);
2073                                 g_free(g_dest);
2074                                 return TRUE; /* we're done */
2075                         }
2076                 }
2077
2078                 for(j = 0; j < NUM_COLS; j++) {
2079                         if (j == 0) {
2080                                 fprintf(fp,"\"%s\"",titles[j]);
2081                         } else {
2082                                 fprintf(fp,",\"%s\"",titles[j]);
2083                         }
2084                 }
2085                 fprintf(fp,"\n");
2086                 if (ferror(fp)) {
2087                         write_failure_alert_box(g_dest, errno);
2088                         fclose(fp);
2089                         g_free(g_dest);
2090                         return TRUE;
2091                 }
2092                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2093                 store = GTK_LIST_STORE(model);
2094                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2095
2096                          while (more_items){
2097                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2098                                                     PACKET_COLUMN,    &packet,
2099                                                     SEQUENCE_COLUMN,  &sequence,
2100                                                     TIMESTAMP_COLUMN, &timestamp,
2101                                                     DELTA_COLUMN,     &delta,
2102                                                     JITTER_COLUMN,    &jitter,
2103                                                     SKEW_COLUMN,      &skew,
2104                                                     IPBW_COLUMN,      &ipbw,
2105                                                     MARKER_COLUMN,    &marker,
2106                                                     STATUS_COLUMN,    &status_str,
2107                                                     DATE_COLUMN,      &date_str,
2108                                                     LENGTH_COLUMN,    &length,
2109                                          -1);
2110                                  fprintf(fp, "\"%u\"",    packet);
2111                                  fprintf(fp, ",\"%u\"",   sequence);
2112                                  fprintf(fp, ",\"%u\"",   timestamp);
2113                                  fprintf(fp, ",\"%.2f\"", delta);
2114                                  fprintf(fp, ",\"%.2f\"", jitter);
2115                                  fprintf(fp, ",\"%.2f\"", skew);
2116                                  fprintf(fp, ",\"%.2f\"", ipbw);
2117                                  fprintf(fp, ",\"%s\"",   marker? "SET" : "");
2118                                  fprintf(fp, ",\"%s\"",   status_str);
2119                                  fprintf(fp, ",\"%s\"",   date_str);
2120                                  fprintf(fp, ",\"%u\"",   length);
2121                                  fprintf(fp,"\n");
2122                                  g_free(status_str);
2123                                  g_free(date_str);
2124                                  if (ferror(fp)) {
2125                                          write_failure_alert_box(g_dest, errno);
2126                                          fclose(fp);
2127                                          g_free(g_dest);
2128                                          return TRUE; /* we're done */
2129                                  }
2130
2131                                  more_items = gtk_tree_model_iter_next (model,&iter);
2132                          }
2133                  }
2134
2135                 if (fclose(fp) == EOF) {
2136                         write_failure_alert_box(g_dest, errno);
2137                         g_free(g_dest);
2138                         return TRUE; /* we're done */
2139                 }
2140         }
2141
2142         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2143
2144                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2145                         fp = ws_fopen(g_dest, "a");
2146                         if (fp == NULL) {
2147                                 open_failure_alert_box(g_dest, errno, TRUE);
2148                                 g_free(g_dest);
2149                                 return TRUE; /* we're done */
2150                         }
2151                         fprintf(fp, "\nReverse\n");
2152                         if (ferror(fp)) {
2153                                 write_failure_alert_box(g_dest, errno);
2154                                 fclose(fp);
2155                                 g_free(g_dest);
2156                                 return TRUE; /* we're done */
2157                         }
2158                 } else {
2159                         fp = ws_fopen(g_dest, "w");
2160                         if (fp == NULL) {
2161                                 open_failure_alert_box(g_dest, errno, TRUE);
2162                                 g_free(g_dest);
2163                                 return TRUE; /* we're done */
2164                         }
2165                 }
2166                 for(j = 0; j < NUM_COLS; j++) {
2167                         if (j == 0) {
2168                                 fprintf(fp,"\"%s\"",titles[j]);
2169                         } else {
2170                                 fprintf(fp,",\"%s\"",titles[j]);
2171                         }
2172                 }
2173                 fprintf(fp,"\n");
2174                 if (ferror(fp)) {
2175                         write_failure_alert_box(g_dest, errno);
2176                         fclose(fp);
2177                         g_free(g_dest);
2178                         return TRUE; /* we're done */
2179                 }
2180                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2181                 store = GTK_LIST_STORE(model);
2182                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2183
2184                         more_items = TRUE;
2185
2186                          while (more_items){
2187                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2188                                          PACKET_COLUMN,    &packet,
2189                                          SEQUENCE_COLUMN,  &sequence,
2190                                          TIMESTAMP_COLUMN, &timestamp,
2191                                          DELTA_COLUMN,     &delta,
2192                                          JITTER_COLUMN,    &jitter,
2193                                          SKEW_COLUMN,      &skew,
2194                                          IPBW_COLUMN,      &ipbw,
2195                                          MARKER_COLUMN,    &marker,
2196                                          STATUS_COLUMN,    &status_str,
2197                                          DATE_COLUMN,      &date_str,
2198                                          LENGTH_COLUMN,    &length,
2199                                          -1);
2200                                  fprintf(fp, "\"%u\"",    packet);
2201                                  fprintf(fp, ",\"%u\"",   sequence);
2202                                  fprintf(fp, ",\"%u\"",   timestamp);
2203                                  fprintf(fp, ",\"%.2f\"", delta);
2204                                  fprintf(fp, ",\"%.2f\"", jitter);
2205                                  fprintf(fp, ",\"%.2f\"", skew);
2206                                  fprintf(fp, ",\"%.2f\"", ipbw);
2207                                  fprintf(fp, ",\"%s\"",   marker? "SET" : "");
2208                                  fprintf(fp, ",\"%s\"",   status_str);
2209                                  fprintf(fp, ",\"%s\"",   date_str);
2210                                  fprintf(fp, ",\"%u\"",   length);
2211                                  fprintf(fp,"\n");
2212                                  g_free(status_str);
2213                                  g_free(date_str);
2214                                  if (ferror(fp)) {
2215                                          write_failure_alert_box(g_dest, errno);
2216                                          fclose(fp);
2217                                          g_free(g_dest);
2218                                          return TRUE; /* we're done */
2219                                  }
2220
2221                                  more_items = gtk_tree_model_iter_next (model,&iter);
2222                          }
2223                  }
2224                 if (fclose(fp) == EOF) {
2225                         write_failure_alert_box(g_dest, errno);
2226                         g_free(g_dest);
2227                         return TRUE; /* we're done */
2228                 }
2229         }
2230
2231         g_free(g_dest);
2232         return TRUE; /* we're done */
2233 }
2234
2235 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2236 {
2237         user_data->dlg.save_csv_as_w = NULL;
2238 }
2239
2240 /* when the user wants to save the csv information in a file */
2241 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2242 {
2243         GtkWidget *vertb;
2244         GtkWidget *table1;
2245         GtkWidget *label_format;
2246         GtkWidget *channels_label;
2247         GtkWidget *forward_rb;
2248         GtkWidget *reversed_rb;
2249         GtkWidget *both_rb;
2250
2251 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2252         if (user_data->dlg.save_csv_as_w != NULL) {
2253                 /* There's already a Save CSV info dialog box; reactivate it. */
2254                 reactivate_window(user_data->dlg.save_csv_as_w);
2255                 return;
2256         }
2257 #endif
2258         user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2259                                                                    GTK_WINDOW(user_data->dlg.notebook),
2260                                                                    GTK_FILE_CHOOSER_ACTION_SAVE,
2261                                                                    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2262                                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2263                                                                    NULL);
2264         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2265         gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2266
2267         /* Container for each row of widgets */
2268         vertb = gtk_vbox_new(FALSE, 0);
2269         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2270         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2271         gtk_widget_show (vertb);
2272
2273         table1 = gtk_table_new (2, 4, FALSE);
2274         gtk_widget_show (table1);
2275         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2276         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2277         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2278
2279         label_format = gtk_label_new ("Format: Comma Separated Values");
2280         gtk_widget_show (label_format);
2281         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2282                 (GtkAttachOptions) (GTK_FILL),
2283                 (GtkAttachOptions) (0), 0, 0);
2284
2285         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2286
2287
2288         channels_label = gtk_label_new ("Channels:    ");
2289         gtk_widget_show (channels_label);
2290         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2291                 (GtkAttachOptions) (GTK_FILL),
2292                 (GtkAttachOptions) (0), 0, 0);
2293         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2294
2295         forward_rb = gtk_radio_button_new_with_label (NULL, "forward  ");
2296         gtk_widget_show (forward_rb);
2297         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2298                 (GtkAttachOptions) (GTK_FILL),
2299                 (GtkAttachOptions) (0), 0, 0);
2300
2301         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
2302         gtk_widget_show (reversed_rb);
2303         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2304                 (GtkAttachOptions) (GTK_FILL),
2305                 (GtkAttachOptions) (0), 0, 0);
2306
2307         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2308         gtk_widget_show (both_rb);
2309         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2310                 (GtkAttachOptions) (GTK_FILL),
2311                 (GtkAttachOptions) (0), 0, 0);
2312
2313         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2314
2315         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2316         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2317         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2318         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2319
2320         g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2321                 G_CALLBACK(window_delete_event_cb), NULL);
2322         g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2323                 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2324
2325         gtk_widget_show(user_data->dlg.save_csv_as_w);
2326         window_present(user_data->dlg.save_csv_as_w);
2327
2328         /* "Run" the GtkFileChooserDialog.                                              */
2329         /* Upon exit: If "Accept" run the OK callback.                                  */
2330         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
2331         /*            Destroy the window.                                               */
2332         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
2333         /*      return with a TRUE status so that the dialog window will be destroyed.  */
2334         /*      Trying to re-run the dialog after popping up an alert box will not work */
2335         /*       since the user will not be able to dismiss the alert box.              */
2336         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
2337         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
2338         /*                                                                              */
2339         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
2340         /*            GtkFileChooserDialog.                                             */
2341         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2342                 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2343                         break; /* we're done */
2344                 }
2345         }
2346         window_destroy(user_data->dlg.save_csv_as_w);
2347 }
2348
2349
2350 /****************************************************************************/
2351 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2352 {
2353         /* Note that we no longer have a Save voice info dialog box. */
2354         user_data->dlg.save_voice_as_w = NULL;
2355 }
2356
2357 /****************************************************************************/
2358 /* here we save it into a file that user specified */
2359 /* XXX what about endians here? could go something wrong? */
2360
2361 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2362 {
2363         FILE *to_stream, *forw_stream, *rev_stream;
2364         size_t fwritten, rwritten;
2365         int f_rawvalue, r_rawvalue, rawvalue;
2366         gint16 sample;
2367         gchar pd[4];
2368         guint32 f_write_silence = 0;
2369         guint32 r_write_silence = 0;
2370         progdlg_t *progbar;
2371         guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2372         gboolean stop_flag = FALSE;
2373         size_t nchars;
2374         gboolean ret_val;
2375
2376         forw_stream = ws_fopen(user_data->f_tempname, "rb");
2377         if (forw_stream == NULL)
2378                 return FALSE;
2379         rev_stream = ws_fopen(user_data->r_tempname, "rb");
2380         if (rev_stream == NULL) {
2381                 fclose(forw_stream);
2382                 return FALSE;
2383         }
2384
2385         /* open file for saving */
2386         to_stream = ws_fopen(dest, "wb");
2387         if (to_stream == NULL) {
2388                 fclose(forw_stream);
2389                 fclose(rev_stream);
2390                 return FALSE;
2391         }
2392
2393         progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2394
2395         if      (format == SAVE_AU_FORMAT) /* au format */
2396         {
2397                 /* First we write the .au header. XXX Hope this is endian independent */
2398                 /* the magic word 0x2e736e64 == .snd */
2399                 phtonl(pd, 0x2e736e64);
2400                 nchars = fwrite(pd, 1, 4, to_stream);
2401                 if (nchars != 4)
2402                         goto copy_file_err;
2403                 /* header offset == 24 bytes */
2404                 phtonl(pd, 24);
2405                 nchars = fwrite(pd, 1, 4, to_stream);
2406                 if (nchars != 4)
2407                         goto copy_file_err;
2408                 /* total length; it is permitted to set this to 0xffffffff */
2409                 phtonl(pd, -1);
2410                 nchars = fwrite(pd, 1, 4, to_stream);
2411                 if (nchars != 4)
2412                         goto copy_file_err;
2413                 /* encoding format == 16-bit linear PCM */
2414                 phtonl(pd, 3);
2415                 nchars = fwrite(pd, 1, 4, to_stream);
2416                 if (nchars != 4)
2417                         goto copy_file_err;
2418                 /* sample rate == 8000 Hz */
2419                 phtonl(pd, 8000);
2420                 nchars = fwrite(pd, 1, 4, to_stream);
2421                 if (nchars != 4)
2422                         goto copy_file_err;
2423                 /* channels == 1 */
2424                 phtonl(pd, 1);
2425                 nchars = fwrite(pd, 1, 4, to_stream);
2426                 if (nchars != 4)
2427                         goto copy_file_err;
2428
2429
2430                 switch (channels) {
2431                         /* only forward direction */
2432                         case SAVE_FORWARD_DIRECTION_MASK: {
2433                                 progbar_count = user_data->forward.saveinfo.count;
2434                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2435                                 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2436                                         if(stop_flag)
2437                                                 break;
2438                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2439                                                 update_progress_dlg(progbar,
2440                                                         (gfloat) count/progbar_count, "Saving");
2441                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2442                                         }
2443                                         count++;
2444
2445                                         if (user_data->forward.statinfo.pt == PT_PCMU){
2446                                                 sample = ulaw2linear((unsigned char)f_rawvalue);
2447                                                 phtons(pd, sample);
2448                                         }
2449                                         else if(user_data->forward.statinfo.pt == PT_PCMA){
2450                                                 sample = alaw2linear((unsigned char)f_rawvalue);
2451                                                 phtons(pd, sample);
2452                                         }
2453                                         else{
2454                                                 goto copy_file_err;
2455                                         }
2456
2457                                         fwritten = fwrite(pd, 1, 2, to_stream);
2458                                         if (fwritten < 2) {
2459                                                 goto copy_file_err;
2460                                         }
2461                                 }
2462                                 break;
2463                         }
2464                         /* only reversed direction */
2465                         case SAVE_REVERSE_DIRECTION_MASK: {
2466                                 progbar_count = user_data->reversed.saveinfo.count;
2467                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2468                                 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2469                                         if(stop_flag)
2470                                                 break;
2471                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2472                                                 update_progress_dlg(progbar,
2473                                                         (gfloat) count/progbar_count, "Saving");
2474                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2475                                         }
2476                                         count++;
2477
2478                                         if (user_data->reversed.statinfo.pt == PT_PCMU){
2479                                                 sample = ulaw2linear((unsigned char)r_rawvalue);
2480                                                 phtons(pd, sample);
2481                                         }
2482                                         else if(user_data->reversed.statinfo.pt == PT_PCMA){
2483                                                 sample = alaw2linear((unsigned char)r_rawvalue);
2484                                                 phtons(pd, sample);
2485                                         }
2486                                         else{
2487                                                 goto copy_file_err;
2488                                         }
2489
2490                                         rwritten = fwrite(pd, 1, 2, to_stream);
2491                                         if (rwritten < 2) {
2492                                                 goto copy_file_err;
2493                                         }
2494                                 }
2495                                 break;
2496                         }
2497                         /* both directions */
2498                         case SAVE_BOTH_DIRECTION_MASK: {
2499                                 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2500                                                 (progbar_count = user_data->forward.saveinfo.count) :
2501                                                         (progbar_count = user_data->reversed.saveinfo.count);
2502                                 progbar_quantum = progbar_count/100;
2503                                 /* since conversation in one way can start later than in the other one,
2504                                  * we have to write some silence information for one channel */
2505                                 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2506                                         f_write_silence = (guint32)
2507                                                 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*(8000/1000));
2508                                 }
2509                                 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2510                                         r_write_silence = (guint32)
2511                                                 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*(8000/1000));
2512                                 }
2513                                 for(;;) {
2514                                         if(stop_flag)
2515                                                 break;
2516                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2517                                                 update_progress_dlg(progbar,
2518                                                         (gfloat) count/progbar_count, "Saving");
2519                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2520                                         }
2521                                         count++;
2522                                         if(f_write_silence > 0) {
2523                                                 r_rawvalue = getc(rev_stream);
2524                                                 switch (user_data->forward.statinfo.reg_pt) {
2525                                                 case PT_PCMU:
2526                                                         f_rawvalue = SILENCE_PCMU;
2527                                                         break;
2528                                                 case PT_PCMA:
2529                                                         f_rawvalue = SILENCE_PCMA;
2530                                                         break;
2531                                                 default:
2532                                                         f_rawvalue = 0;
2533                                                         break;
2534                                                 }
2535                                                 f_write_silence--;
2536                                         }
2537                                         else if(r_write_silence > 0) {
2538                                                 f_rawvalue = getc(forw_stream);
2539                                                 switch (user_data->reversed.statinfo.reg_pt) {
2540                                                 case PT_PCMU:
2541                                                         r_rawvalue = SILENCE_PCMU;
2542                                                         break;
2543                                                 case PT_PCMA:
2544                                                         r_rawvalue = SILENCE_PCMA;
2545                                                         break;
2546                                                 default:
2547                                                         r_rawvalue = 0;
2548                                                         break;
2549                                                 }
2550                                                 r_write_silence--;
2551                                         }
2552                                         else {
2553                                                 f_rawvalue = getc(forw_stream);
2554                                                 r_rawvalue = getc(rev_stream);
2555                                         }
2556                                         if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2557                                                 break;
2558                                         if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2559                                                 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2560                                                 phtons(pd, sample);
2561                                         }
2562                                         else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2563                                                 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2564                                                 phtons(pd, sample);
2565                                         }
2566                                         else
2567                                         {
2568                                                 goto copy_file_err;
2569                                         }
2570
2571
2572                                         rwritten = fwrite(pd, 1, 2, to_stream);
2573                                         if (rwritten < 2) {
2574                                                 goto copy_file_err;
2575                                         }
2576                                 }
2577                         }
2578                 }
2579         }
2580         else if (format == SAVE_RAW_FORMAT)     /* raw format */
2581         {
2582                 FILE *stream;
2583                 switch (channels) {
2584                         /* only forward direction */
2585                         case SAVE_FORWARD_DIRECTION_MASK: {
2586                                 progbar_count = user_data->forward.saveinfo.count;
2587                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2588                                 stream = forw_stream;
2589                                 break;
2590                         }
2591                         /* only reversed direction */
2592                         case SAVE_REVERSE_DIRECTION_MASK: {
2593                                 progbar_count = user_data->reversed.saveinfo.count;
2594                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2595                                 stream = rev_stream;
2596                                 break;
2597                         }
2598                         default: {
2599                                 goto copy_file_err;
2600                         }
2601                 }
2602
2603
2604
2605                 /* XXX how do you just copy the file? */
2606                 while ((rawvalue = getc(stream)) != EOF) {
2607                         if(stop_flag)
2608                                 break;
2609                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2610                                 update_progress_dlg(progbar,
2611                                         (gfloat) count/progbar_count, "Saving");
2612                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2613                         }
2614                         count++;
2615
2616                         if (putc(rawvalue, to_stream) == EOF) {
2617                                 goto copy_file_err;
2618                         }
2619                 }
2620         }
2621
2622         ret_val = TRUE;
2623         goto copy_file_xit;
2624
2625 copy_file_err:
2626         ret_val = FALSE;
2627         goto copy_file_xit;
2628
2629 copy_file_xit:
2630         destroy_progress_dlg(progbar);
2631         fclose(forw_stream);
2632         fclose(rev_stream);
2633         fclose(to_stream);
2634         return ret_val;
2635 }
2636
2637
2638 /****************************************************************************/
2639 /* the user wants to save in a file */
2640 /* XXX support for different formats is currently commented out */
2641 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2642 {
2643         gchar *g_dest;
2644         /*GtkWidget *wav, *sw;*/
2645         GtkWidget *au, *raw;
2646         GtkWidget *rev, *forw, *both;
2647         user_data_t *user_data;
2648         gint channels, format;
2649
2650         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2651
2652         /* Perhaps the user specified a directory instead of a file.
2653          * Check whether they did.
2654          */
2655         if (test_for_directory(g_dest) == EISDIR) {
2656                 /* It's a directory - set the file selection box to display it. */
2657                 set_last_open_dir(g_dest);
2658                 g_free(g_dest);
2659                 file_selection_set_current_folder(fc, get_last_open_dir());
2660                 gtk_file_chooser_set_current_name(fc, "");
2661                 return FALSE; /* run the dialog again */
2662         }
2663
2664 #if 0
2665         wav  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2666         sw   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2667 #endif
2668         au   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2669         raw  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2670         rev  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2671         forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2672         both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2673         user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2674
2675         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2676         * we don't support that codec. So we pop up a warning. Maybe it would be better to
2677         * disable the ok button or disable the buttons for direction if only one is not ok. The
2678         * problem is if we open the save voice dialog and then click the refresh button and maybe
2679         * the state changes, so we can't save anymore. In this case we should be able to update
2680         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2681         */
2682
2683         /* we can not save in both directions */
2684         if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2685                 /* there are many combinations here, we just exit when first matches */
2686                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2687                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2688                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2689                         "Can't save in a file: Unsupported codec!");
2690                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2691                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2692                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2693                         "Can't save in a file: Wrong length of captured packets!");
2694                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2695                         (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2696                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2697                         "Can't save in a file: RTP data with padding!");
2698                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2699                         (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2700                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2701                         "Can't save in a file: Not all data in all packets was captured!");
2702                 else
2703                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2704                         "Can't save in a file: File I/O problem!");
2705                 g_free(g_dest);
2706                 return TRUE; /* we're done */
2707         }
2708         /* we can not save forward direction */
2709         else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
2710                 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2711                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2712                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2713                         "Can't save forward direction in a file: Unsupported codec!");
2714                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2715                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2716                         "Can't save forward direction in a file: Wrong length of captured packets!");
2717                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2718                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2719                         "Can't save forward direction in a file: RTP data with padding!");
2720                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2721                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2722                         "Can't save forward direction in a file: Not all data in all packets was captured!");
2723                 else
2724                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2725                         "Can't save forward direction in a file: File I/O problem!");
2726                 g_free(g_dest);
2727                 return TRUE; /* we're done */
2728         }
2729         /* we can not save reversed direction */
2730         else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
2731                 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2732                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2733                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2734                         "Can't save reversed direction in a file: Unsupported codec!");
2735                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2736                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2737                         "Can't save reversed direction in a file: Wrong length of captured packets!");
2738                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2739                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2740                         "Can't save reversed direction in a file: RTP data with padding!");
2741                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2742                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2743                         "Can't save reversed direction in a file: Not all data in all packets was captured!");
2744                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2745                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2746                         "Can't save reversed direction in a file: No RTP data!");
2747                 else
2748                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2749                         "Can't save reversed direction in a file: File I/O problem!");
2750                 g_free(g_dest);
2751                 return TRUE; /* we're done */
2752         }
2753
2754 #if 0
2755         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2756                 format = SAVE_WAV_FORMAT;
2757         else
2758 #endif
2759         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2760                 format = SAVE_AU_FORMAT;
2761 #if 0
2762         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2763                 format = SAVE_SW_FORMAT;
2764 #endif
2765         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2766                 format = SAVE_RAW_FORMAT;
2767         else
2768                 format = SAVE_NONE_FORMAT;
2769
2770         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2771                 channels = SAVE_REVERSE_DIRECTION_MASK;
2772         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2773                 channels = SAVE_BOTH_DIRECTION_MASK;
2774         else
2775                 channels = SAVE_FORWARD_DIRECTION_MASK;
2776
2777         /* direction/format validity*/
2778         if (format == SAVE_AU_FORMAT)
2779         {
2780                 /* make sure streams are alaw/ulaw */
2781                 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2782                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2783                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2784                         g_free(g_dest);
2785                         return TRUE; /* we're done */
2786                 }
2787                 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2788                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2789                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2790                         g_free(g_dest);
2791                         return TRUE; /* we're done */
2792                 }
2793                 /* make sure pt's don't differ */
2794                 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2795                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2796                                 "Can't save in a file: Forward and reverse direction differ in type");
2797                         g_free(g_dest);
2798                         return TRUE; /* we're done */
2799                 }
2800         }
2801         else if (format == SAVE_RAW_FORMAT)
2802         {
2803                 /* can't save raw in both directions */
2804                 if (channels == SAVE_BOTH_DIRECTION_MASK){
2805                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2806                                 "Can't save in a file: Unable to save raw data in both directions");
2807                         g_free(g_dest);
2808                         return TRUE; /* we're done */
2809                 }
2810         }
2811         else
2812         {
2813                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2814                         "Can't save in a file: Invalid save format");
2815                 g_free(g_dest);
2816                 return TRUE; /* we're done */
2817         }
2818
2819         if(!copy_file(g_dest, channels, format, user_data)) {
2820                 /* XXX - report the error type! */
2821                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2822                         "An error occurred while saving voice in a file!");
2823                 g_free(g_dest);
2824                 return TRUE; /* we're done */
2825         }
2826
2827         g_free(g_dest);
2828         return TRUE; /* we're done */
2829 }
2830
2831 /****************************************************************************/
2832 /* when the user wants to save the voice information in a file */
2833 /* XXX support for different formats is currently commented out */
2834 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2835 {
2836         GtkWidget *vertb;
2837         GtkWidget *table1;
2838         GtkWidget *label_format;
2839         GtkWidget *channels_label;
2840         GtkWidget *forward_rb;
2841         GtkWidget *reversed_rb;
2842         GtkWidget *both_rb;
2843         /*GtkWidget *wav_rb;  GtkWidget *sw_rb;*/
2844         GtkWidget *au_rb;
2845         GtkWidget *raw_rb;
2846
2847         /* if we can't save in a file: wrong codec, cut packets or other errors */
2848         /* Should the error arise here or later when you click ok button ?
2849          * if we do it here, then we must disable the refresh button, so we don't do it here
2850          */
2851
2852 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2853         if (user_data->dlg.save_voice_as_w != NULL) {
2854                 /* There's already a Save voice info dialog box; reactivate it. */
2855                 reactivate_window(user_data->dlg.save_voice_as_w);
2856                 return;
2857         }
2858 #endif
2859         /* XXX - use file_selection from dlg_utils instead! */
2860         user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2861                                                                      GTK_WINDOW(user_data->dlg.notebook),
2862                                                                      GTK_FILE_CHOOSER_ACTION_SAVE,
2863                                                                      GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2864                                                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2865                                                                      NULL);
2866         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2867         gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2868
2869         /* Container for each row of widgets */
2870         vertb = gtk_vbox_new(FALSE, 0);
2871         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2872         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2873         gtk_widget_show (vertb);
2874
2875         table1 = gtk_table_new (2, 4, FALSE);
2876         gtk_widget_show (table1);
2877         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2878         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2879         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2880
2881         /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2882         gtk_widget_show (label_format);
2883         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2884                 (GtkAttachOptions) (GTK_FILL),
2885                 (GtkAttachOptions) (0), 0, 0);*/
2886
2887         label_format = gtk_label_new ("Format: ");
2888         gtk_widget_show (label_format);
2889         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2890                 (GtkAttachOptions) (GTK_FILL),
2891                 (GtkAttachOptions) (0), 0, 0);
2892
2893         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2894
2895         raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2896         gtk_widget_show (raw_rb);
2897         gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2898         (GtkAttachOptions) (GTK_FILL),
2899         (GtkAttachOptions) (0), 0, 0);
2900
2901
2902         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2903         gtk_widget_show (au_rb);
2904         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2905         (GtkAttachOptions) (GTK_FILL),
2906         (GtkAttachOptions) (0), 0, 0);
2907
2908 #if 0
2909         /* we support .au - ulaw*/
2910         wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
2911         gtk_widget_show (wav_rb);
2912         gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2913         (GtkAttachOptions) (GTK_FILL),
2914         (GtkAttachOptions) (0), 0, 0);
2915
2916         sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit  ");
2917         gtk_widget_show (sw_rb);
2918         gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2919                           (GtkAttachOptions) (GTK_FILL),
2920                           (GtkAttachOptions) (0), 0, 0);
2921         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2922         gtk_widget_show (au_rb);
2923         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2924                           (GtkAttachOptions) (GTK_FILL),
2925                           (GtkAttachOptions) (0), 0, 0);
2926 #endif
2927
2928         channels_label = gtk_label_new ("Channels:    ");
2929         gtk_widget_show (channels_label);
2930         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2931                 (GtkAttachOptions) (GTK_FILL),
2932                 (GtkAttachOptions) (0), 0, 0);
2933         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2934
2935         forward_rb = gtk_radio_button_new_with_label (NULL, "forward    ");
2936         gtk_widget_show (forward_rb);
2937         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2938                 (GtkAttachOptions) (GTK_FILL),
2939                 (GtkAttachOptions) (0), 0, 0);
2940
2941         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
2942         gtk_widget_show (reversed_rb);
2943         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2944                           (GtkAttachOptions) (GTK_FILL),
2945                           (GtkAttachOptions) (0), 0, 0);
2946
2947         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2948         gtk_widget_show (both_rb);
2949         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2950                           (GtkAttachOptions) (GTK_FILL),
2951                           (GtkAttachOptions) (0), 0, 0);
2952
2953
2954         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2955
2956 #if 0
2957         /* if one direction is nok we don't allow saving
2958         XXX this is not ok since the user can click the refresh button and cause changes
2959         but we can not update this window. So we move all the decision on the time the ok
2960         button is clicked
2961         */
2962         if (user_data->forward.saved == FALSE) {
2963         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2964         gtk_widget_set_sensitive(forward_rb, FALSE);
2965         gtk_widget_set_sensitive(both_rb, FALSE);
2966         }
2967         else if (user_data->reversed.saved == FALSE) {
2968         gtk_widget_set_sensitive(reversed_rb, FALSE);
2969         gtk_widget_set_sensitive(both_rb, FALSE);
2970         }
2971  #endif
2972
2973         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2974         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2975         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2976         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2977         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2978         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2979         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2980         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2981
2982         g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2983                          G_CALLBACK(window_delete_event_cb), NULL);
2984         g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2985                          G_CALLBACK(save_voice_as_destroy_cb), user_data);
2986
2987         gtk_widget_show(user_data->dlg.save_voice_as_w);
2988         window_present(user_data->dlg.save_voice_as_w);
2989
2990         /* "Run" the GtkFileChooserDialog.                                              */
2991         /* Upon exit: If "Accept" run the OK callback.                                  */
2992         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
2993         /*            Destroy the window.                                               */
2994         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
2995         /*      return with a TRUE status so that the dialog window will be destroyed.  */
2996         /*      Trying to re-run the dialog after popping up an alert box will not work */
2997         /*       since the user will not be able to dismiss the alert box.              */
2998         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
2999         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
3000         /*                                                                              */
3001         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
3002         /*            GtkFileChooserDialog.                                             */
3003         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
3004                 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
3005                         break;  /* we're done */
3006                 }
3007         }
3008         window_destroy(user_data->dlg.save_voice_as_w);
3009
3010 }
3011
3012
3013 /****************************************************************************/
3014 /* when we are finished with redisection, we add the label for the statistic */
3015 static void draw_stat(user_data_t *user_data)
3016 {
3017         gchar label_max[300];
3018         guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3019                 - user_data->forward.statinfo.start_seq_nr + 1;
3020         guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3021                 -&