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