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