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