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