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