Get rid of a Clist and move the functon to display floats with two decimals to gui_utils.
[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*1000,
596                         statinfo->jitter*1000,
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, 80, "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, 80, "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, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
902                                         graph_descr[i],
903                         get_addr_name(&(user_data->ip_src_fwd)),
904                         user_data->port_src_fwd,
905                         get_addr_name(&(user_data->ip_dst_fwd)),
906                         user_data->port_dst_fwd,
907                         user_data->ssrc_fwd);
908                 /* it is reverse */
909                 } else {
910                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
911                                         graph_descr[i],
912                         get_addr_name(&(user_data->ip_src_rev)),
913                         user_data->port_src_rev,
914                         get_addr_name(&(user_data->ip_dst_rev)),
915                         user_data->port_dst_rev,
916                         user_data->ssrc_rev);
917                 }
918         }
919
920         dialog_graph_set_title(user_data);
921 }
922
923 /****************************************************************************/
924 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
925 {
926         dialog_graph_graph_item_t *it;
927
928         it=&dgg->items[idx];
929
930         return it->value;
931 }
932
933 /****************************************************************************/
934 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
935 {
936         if(t>=10000000){
937                 g_snprintf(buf, buf_len, "%ds",t/1000000);
938         } else if(t>=1000000){
939                 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
940         } else if(t>=10000){
941                 g_snprintf(buf, buf_len, "%dms",t/1000);
942         } else if(t>=1000){
943                 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
944         } else {
945                 g_snprintf(buf, buf_len, "%dus",t);
946         }
947 }
948
949 /****************************************************************************/
950 static void dialog_graph_draw(user_data_t* user_data)
951 {
952         int i, lwidth;
953         guint32 last_interval, first_interval, interval_delta, delta_multiplier;
954         gint32 current_interval;
955         guint32 left_x_border;
956         guint32 right_x_border;
957         guint32 top_y_border;
958         guint32 bottom_y_border;
959         PangoLayout  *layout;
960         int label_width, label_height;
961         guint32 draw_width, draw_height;
962         char label_string[15];
963
964         /* new variables */
965         guint32 num_time_intervals;
966         guint32 max_value;              /* max value of seen data */
967         guint32 max_y;                  /* max value of the Y scale */
968
969         if(!user_data->dlg.dialog_graph.needs_redraw){
970                 return;
971         }
972         user_data->dlg.dialog_graph.needs_redraw=FALSE;
973
974         /*
975          * Find the length of the intervals we have data for
976          * so we know how large arrays we need to malloc()
977          */
978         num_time_intervals=user_data->dlg.dialog_graph.num_items;
979         /* if there isnt anything to do, just return */
980         if(num_time_intervals==0){
981                 return;
982         }
983         num_time_intervals+=1;
984         /* XXX move this check to _packet() */
985         if(num_time_intervals>NUM_GRAPH_ITEMS){
986                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
987                 return;
988         }
989
990         /*
991          * find the max value so we can autoscale the y axis
992          */
993         max_value=0;
994         for(i=0;i<MAX_GRAPHS;i++){
995                 int idx;
996
997                 if(!user_data->dlg.dialog_graph.graph[i].display){
998                         continue;
999                 }
1000                 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1001                         guint32 val;
1002
1003                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1004
1005                         /* keep track of the max value we have encountered */
1006                         if(val>max_value){
1007                                 max_value=val;
1008                         }
1009                 }
1010         }
1011
1012         /*
1013          * Clear out old plot
1014          */
1015         gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1016                            user_data->dlg.dialog_graph.draw_area->style->white_gc,
1017                            TRUE,
1018                            0, 0,
1019                            user_data->dlg.dialog_graph.draw_area->allocation.width,
1020                            user_data->dlg.dialog_graph.draw_area->allocation.height);
1021
1022
1023         /*
1024          * Calculate the y scale we should use
1025          */
1026         if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1027                 max_y=yscale_max[MAX_YSCALE-1];
1028                 for(i=MAX_YSCALE-1;i>0;i--){
1029                         if(max_value<yscale_max[i]){
1030                                 max_y=yscale_max[i];
1031                         }
1032                 }
1033         } else {
1034                 /* the user had specified an explicit y scale to use */
1035                 max_y=user_data->dlg.dialog_graph.max_y_units;
1036         }
1037
1038         /*
1039          * Calculate size of borders surrounding the plot
1040          * The border on the right side needs to be adjusted depending
1041          * on the width of the text labels. For simplicity we assume that the
1042          * top y scale label will be the widest one
1043          */
1044          print_time_scale_string(label_string, 15, max_y);
1045         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1046         pango_layout_get_pixel_size(layout, &label_width, &label_height);
1047         left_x_border=10;
1048         right_x_border=label_width+20;
1049         top_y_border=10;
1050         bottom_y_border=label_height+20;
1051
1052
1053         /*
1054          * Calculate the size of the drawing area for the actual plot
1055          */
1056         draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1057         draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1058
1059
1060         /*
1061          * Draw the y axis and labels
1062          * (we always draw the y scale with 11 ticks along the axis)
1063          */
1064         gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1065                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1066                 top_y_border,
1067                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1068                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1069         for(i=0;i<=10;i++){
1070                 int xwidth, lwidth;
1071
1072                 xwidth=5;
1073                 if(!(i%5)){
1074                         /* first, middle and last tick are slightly longer */
1075                         xwidth=10;
1076                 }
1077                 /* draw the tick */
1078                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1079                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1080                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1081                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1082                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1083                 /* draw the labels */
1084                 if(i==0){
1085                         print_time_scale_string(label_string, 15, (max_y*i/10));
1086                         pango_layout_set_text(layout, label_string, -1);
1087                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1088                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1089                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1090                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1091                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1092                                         layout);
1093                 }
1094                 if(i==5){
1095                         print_time_scale_string(label_string, 15, (max_y*i/10));
1096                         pango_layout_set_text(layout, label_string, -1);
1097                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1098                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1099                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1100                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1101                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1102                                         layout);
1103                 }
1104                 if(i==10){
1105                         print_time_scale_string(label_string, 15, (max_y*i/10));
1106                         pango_layout_set_text(layout, label_string, -1);
1107                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1108                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1109                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1110                                         user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1111                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1112                                         layout);
1113                 }
1114         }
1115
1116
1117
1118         /*
1119          * if we have not specified the last_interval via the gui,
1120          * then just pick the current end of the capture so that is scrolls
1121          * nicely when doing live captures
1122          */
1123         if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1124                 last_interval=user_data->dlg.dialog_graph.max_interval;
1125         } else {
1126                 last_interval=user_data->dlg.dialog_graph.last_interval;
1127         }
1128
1129
1130
1131
1132 /*XXX*/
1133         /* plot the x-scale */
1134         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);
1135
1136         if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1137                 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1138                 first_interval*=user_data->dlg.dialog_graph.interval;
1139         } else {
1140                 first_interval=0;
1141         }
1142
1143         interval_delta=1;
1144         delta_multiplier=5;
1145         while(interval_delta<((last_interval-first_interval)/10)){
1146                 interval_delta*=delta_multiplier;
1147                 if(delta_multiplier==5){
1148                         delta_multiplier=2;
1149                 } else {
1150                         delta_multiplier=5;
1151                 }
1152         }
1153
1154         for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1155                 int x, xlen;
1156
1157                 /* if pixels_per_tick is <5, only draw every 10 ticks */
1158                 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1159                         continue;
1160                 }
1161
1162                 if(current_interval%interval_delta){
1163                         xlen=5;
1164                 } else {
1165                         xlen=17;
1166                 }
1167
1168                 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1169                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1170                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1171                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1172                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1173                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1174
1175                 if(xlen==17){
1176                         int lwidth;
1177                         if(user_data->dlg.dialog_graph.interval>=1000){
1178                                 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1179                         } else if(user_data->dlg.dialog_graph.interval>=100){
1180                                 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1181                         } else if(user_data->dlg.dialog_graph.interval>=10){
1182                                 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1183                         } else {
1184                                 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1185                         }
1186                         pango_layout_set_text(layout, label_string, -1);
1187                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1188                         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1189                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1190                                         x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1191                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1192                                         layout);
1193                 }
1194
1195         }
1196
1197
1198
1199
1200
1201
1202         /*
1203          * Draw "x" for Sequence Errors and "m" for Marks
1204          */
1205         /* Draw the labels Fwd and Rev */
1206         g_strlcpy(label_string,"<-Fwd",15);
1207         pango_layout_set_text(layout, label_string, -1);
1208         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1209         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1210                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1211                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1212                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1213                 layout);
1214         g_strlcpy(label_string,"<-Rev",15);
1215         pango_layout_set_text(layout, label_string, -1);
1216         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1217         gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1218                 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1219                 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1220                 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1221                 layout);
1222
1223         /* Draw the marks */
1224         for(i=MAX_GRAPHS-1;i>=0;i--){
1225                 guint32 interval;
1226                 guint32 x_pos, prev_x_pos;
1227
1228                 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1229                 if (!user_data->dlg.dialog_graph.graph[i].display){
1230                         continue;
1231                 }
1232                 /* initialize prev x/y to the low left corner of the graph */
1233                 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;
1234
1235                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1236                         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;
1237
1238                         if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1239                                 int lwidth;
1240                                 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1241                                         g_strlcpy(label_string,"x",15);
1242                                 } else {
1243                                         g_strlcpy(label_string,"m",15);
1244                                 }
1245
1246                                 pango_layout_set_text(layout, label_string, -1);
1247                                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1248                                 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1249                                         user_data->dlg.dialog_graph.draw_area->style->black_gc,
1250                                         x_pos-1-lwidth/2,
1251                                         user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1252                                         layout);
1253                         }
1254
1255                         prev_x_pos=x_pos;
1256                 }
1257         }
1258
1259         g_object_unref(G_OBJECT(layout));
1260
1261         /*
1262          * Loop over all graphs and draw them
1263          */
1264         for(i=MAX_GRAPHS-1;i>=0;i--){
1265                 guint32 interval;
1266                 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1267                 if (!user_data->dlg.dialog_graph.graph[i].display){
1268                         continue;
1269                 }
1270                 /* initialize prev x/y to the low left corner of the graph */
1271                 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;
1272                 prev_y_pos=draw_height-1+top_y_border;
1273
1274                 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1275                         guint32 val;
1276                         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;
1277                         val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1278                         if(val>max_y){
1279                                 y_pos=0;
1280                         } else {
1281                                 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1282                         }
1283
1284                         /* dont need to draw anything if the segment
1285                          * is entirely above the top of the graph
1286                          */
1287                         if( (prev_y_pos==0) && (y_pos==0) ){
1288                                 prev_y_pos=y_pos;
1289                                 prev_x_pos=x_pos;
1290                                 continue;
1291                         }
1292
1293                         if(val){
1294                                 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1295                                 x_pos, draw_height-1+top_y_border,
1296                                 x_pos, y_pos);
1297                         }
1298
1299                         prev_y_pos=y_pos;
1300                         prev_x_pos=x_pos;
1301                 }
1302         }
1303
1304
1305         gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1306                         user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1307                         user_data->dlg.dialog_graph.pixmap,
1308                         0, 0,
1309                         0, 0,
1310                         user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1311
1312
1313         /* update the scrollbar */
1314         user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1315         user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1316         user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1317         if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1318                 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1319         } else {
1320                 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1321         }
1322         user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1323         gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1324         gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1325
1326 }
1327
1328 /****************************************************************************/
1329 static void dialog_graph_redraw(user_data_t* user_data)
1330 {
1331         user_data->dlg.dialog_graph.needs_redraw=TRUE;
1332         dialog_graph_draw(user_data);
1333 }
1334
1335 /****************************************************************************/
1336 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1337 {
1338         user_data_t *user_data;
1339
1340         user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1341
1342         user_data->dlg.dialog_graph.window = NULL;
1343         return TRUE;
1344 }
1345
1346 /****************************************************************************/
1347 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1348 {
1349         user_data_t *user_data;
1350
1351         user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1352     if(!user_data){
1353                 exit(10);
1354     }
1355
1356
1357         gdk_draw_pixmap(widget->window,
1358                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1359                         user_data->dlg.dialog_graph.pixmap,
1360                         event->area.x, event->area.y,
1361                         event->area.x, event->area.y,
1362                         event->area.width, event->area.height);
1363
1364         return FALSE;
1365 }
1366
1367 /****************************************************************************/
1368 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1369 {
1370         user_data_t *user_data;
1371         int i;
1372
1373         user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1374
1375         if(!user_data){
1376                 exit(10);
1377         }
1378
1379         if(user_data->dlg.dialog_graph.pixmap){
1380                 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1381                 user_data->dlg.dialog_graph.pixmap=NULL;
1382         }
1383
1384         user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1385                         widget->allocation.width,
1386                         widget->allocation.height,
1387                         -1);
1388         user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1389         user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1390
1391         gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1392                         widget->style->white_gc,
1393                         TRUE,
1394                         0, 0,
1395                         widget->allocation.width,
1396                         widget->allocation.height);
1397
1398         /* set up the colors and the GC structs for this pixmap */
1399         for(i=0;i<MAX_GRAPHS;i++){
1400                 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1401                 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1402         }
1403
1404         dialog_graph_redraw(user_data);
1405         return TRUE;
1406 }
1407
1408 /****************************************************************************/
1409 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1410 {
1411         user_data_t *user_data=(user_data_t *)data;
1412         guint32 mi;
1413
1414         mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1415         if(user_data->dlg.dialog_graph.last_interval==mi){
1416                 return TRUE;
1417         }
1418         if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1419         &&  (mi==user_data->dlg.dialog_graph.max_interval) ){
1420                 return TRUE;
1421         }
1422
1423         user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1424
1425         dialog_graph_redraw(user_data);
1426         return TRUE;
1427 }
1428
1429 /****************************************************************************/
1430 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1431 {
1432         user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1433         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1434         g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1435
1436         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);
1437
1438         /* signals needed to handle backing pixmap */
1439         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1440         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1441
1442         gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1443         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1444
1445         /* create the associated scrollbar */
1446         user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1447         user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1448         gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1449         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1450         g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1451 }
1452
1453 /****************************************************************************/
1454 static void disable_graph(dialog_graph_graph_t *dgg)
1455 {
1456         if (dgg->display) {
1457                 dgg->display=FALSE;
1458                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1459                     FALSE);
1460         }
1461 }
1462
1463 /****************************************************************************/
1464 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1465 {
1466         /* this graph is not active, just update display and redraw */
1467         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1468                 disable_graph(dgg);
1469                 dialog_graph_redraw(dgg->ud);
1470                 return 0;
1471         }
1472
1473                 enable_graph(dgg);
1474         cf_retap_packets(&cfile, FALSE);
1475         dialog_graph_redraw(dgg->ud);
1476
1477         return 0;
1478 }
1479
1480 /****************************************************************************/
1481 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1482 {
1483         GtkWidget *hbox;
1484         GtkWidget *label;
1485         char str[256];
1486
1487         hbox=gtk_hbox_new(FALSE, 3);
1488         gtk_container_add(GTK_CONTAINER(box), hbox);
1489         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1490         gtk_widget_show(hbox);
1491
1492         g_snprintf(str, 256, "Graph %d", num);
1493         dgg->display_button=gtk_toggle_button_new_with_label(str);
1494         gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1495         gtk_widget_show(dgg->display_button);
1496         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1497         g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1498
1499         label=gtk_label_new(dgg->title);
1500         gtk_widget_show(label);
1501         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1502
1503         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1504         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1505         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1506         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1507         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1508
1509         return;
1510 }
1511
1512 /****************************************************************************/
1513 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1514 {
1515         GtkWidget *frame;
1516         GtkWidget *vbox;
1517         int i;
1518         GtkWidget *label;
1519
1520         frame=gtk_frame_new("Graphs");
1521         gtk_container_add(GTK_CONTAINER(box), frame);
1522         gtk_widget_show(frame);
1523
1524         vbox=gtk_vbox_new(FALSE, 1);
1525         gtk_container_add(GTK_CONTAINER(frame), vbox);
1526         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1527         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1528         gtk_widget_show(vbox);
1529
1530         for(i=0;i<MAX_GRAPHS;i++){
1531                 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1532         }
1533
1534         label=gtk_label_new("Label:    x = Wrong Seq. number      m = Mark set");
1535         gtk_widget_show(label);
1536         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1537
1538         return;
1539 }
1540
1541 /****************************************************************************/
1542 static void yscale_select(GtkWidget *item, gpointer key)
1543 {
1544         int val;
1545         user_data_t *user_data;
1546
1547         user_data=(user_data_t *)key;
1548         val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1549
1550         user_data->dlg.dialog_graph.max_y_units=val;
1551         dialog_graph_redraw(user_data);
1552 }
1553
1554 /****************************************************************************/
1555 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1556 {
1557         int val;
1558         user_data_t *user_data;
1559
1560         user_data=(user_data_t *)key;
1561         val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1562         user_data->dlg.dialog_graph.pixels_per_tick=val;
1563         dialog_graph_redraw(user_data);
1564 }
1565
1566 /****************************************************************************/
1567 static void tick_interval_select(GtkWidget *item, gpointer key)
1568 {
1569         int val;
1570         user_data_t *user_data;
1571
1572         user_data=(user_data_t *)key;
1573         val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1574
1575         user_data->dlg.dialog_graph.interval=val;
1576         cf_retap_packets(&cfile, FALSE);
1577         dialog_graph_redraw(user_data);
1578 }
1579
1580 /****************************************************************************/
1581 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1582 {
1583         char str[15];
1584         GtkWidget *menu_item;
1585         int i;
1586
1587         for(i=0;i<MAX_YSCALE;i++){
1588                 if(yscale_max[i]==AUTO_MAX_YSCALE){
1589                         g_strlcpy(str,"Auto",15);
1590                 } else {
1591                         g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1592                 }
1593                 menu_item=gtk_menu_item_new_with_label(str);
1594                 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1595                                 GUINT_TO_POINTER(yscale_max[i]));
1596                 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1597                 gtk_widget_show(menu_item);
1598                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1599         }
1600         return;
1601 }
1602
1603 /****************************************************************************/
1604 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1605 {
1606         char str[5];
1607         GtkWidget *menu_item;
1608         int i;
1609
1610         for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1611                 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1612                 menu_item=gtk_menu_item_new_with_label(str);
1613
1614                 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1615                                 GUINT_TO_POINTER(pixels_per_tick[i]));
1616                 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1617                 gtk_widget_show(menu_item);
1618                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1619         }
1620         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1621         return;
1622 }
1623
1624
1625 /****************************************************************************/
1626 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1627 {
1628         char str[15];
1629         GtkWidget *menu_item;
1630         int i;
1631
1632         for(i=0;i<MAX_TICK_VALUES;i++){
1633                 if(tick_interval_values[i]>=1000){
1634                         g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1635                 } else if(tick_interval_values[i]>=100){
1636                         g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1637                 } else if(tick_interval_values[i]>=10){
1638                         g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1639                 } else {
1640                         g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1641                 }
1642
1643                 menu_item=gtk_menu_item_new_with_label(str);
1644                 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1645                                 GUINT_TO_POINTER(tick_interval_values[i]));
1646                 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1647                 gtk_widget_show(menu_item);
1648                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1649         }
1650         gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1651         return;
1652 }
1653
1654 /****************************************************************************/
1655 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1656 {
1657         GtkWidget *hbox;
1658         GtkWidget *label;
1659         GtkWidget *option_menu;
1660         GtkWidget *menu;
1661
1662         hbox=gtk_hbox_new(FALSE, 0);
1663         gtk_container_add(GTK_CONTAINER(box), hbox);
1664         gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1665         gtk_widget_show(hbox);
1666
1667         label=gtk_label_new(name);
1668         gtk_widget_show(label);
1669         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1670
1671         option_menu=gtk_option_menu_new();
1672         menu=gtk_menu_new();
1673         (*func)(user_data, menu);
1674         gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1675         gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1676         gtk_widget_show(option_menu);
1677 }
1678
1679 /****************************************************************************/
1680 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1681 {
1682         GtkWidget *frame_vbox;
1683         GtkWidget *frame;
1684         GtkWidget *vbox;
1685
1686         frame_vbox=gtk_vbox_new(FALSE, 0);
1687         gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1688         gtk_widget_show(frame_vbox);
1689
1690         frame = gtk_frame_new("X Axis");
1691         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1692         gtk_widget_show(frame);
1693
1694         vbox=gtk_vbox_new(FALSE, 0);
1695         gtk_container_add(GTK_CONTAINER(frame), vbox);
1696         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1697         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1698         gtk_widget_show(vbox);
1699
1700         create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1701         create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1702
1703         frame = gtk_frame_new("Y Axis");
1704         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1705         gtk_widget_show(frame);
1706
1707         vbox=gtk_vbox_new(FALSE, 0);
1708         gtk_container_add(GTK_CONTAINER(frame), vbox);
1709         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1710         gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1711         gtk_widget_show(vbox);
1712
1713         create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1714
1715         return;
1716 }
1717
1718 /****************************************************************************/
1719 static void dialog_graph_init_window(user_data_t* user_data)
1720 {
1721         GtkWidget *vbox;
1722         GtkWidget *hbox;
1723         GtkWidget *bt_close;
1724
1725         /* create the main window */
1726         user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1727
1728         vbox=gtk_vbox_new(FALSE, 0);
1729         gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1730         gtk_widget_show(vbox);
1731
1732         create_draw_area(user_data, vbox);
1733
1734         hbox=gtk_hbox_new(FALSE, 3);
1735         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1736         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1737         gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1738         gtk_widget_show(hbox);
1739
1740         create_filter_area(user_data, hbox);
1741         create_ctrl_area(user_data, hbox);
1742
1743         dialog_graph_set_title(user_data);
1744
1745     hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1746         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1747     gtk_widget_show(hbox);
1748
1749     bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1750     window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1751
1752     g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1753
1754     gtk_widget_show(user_data->dlg.dialog_graph.window);
1755     window_present(user_data->dlg.dialog_graph.window);
1756
1757 }
1758
1759
1760 /****************************************************************************/
1761 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1762 {
1763         if (user_data->dlg.dialog_graph.window != NULL) {
1764                 /* There's already a graph window; reactivate it. */
1765                 reactivate_window(user_data->dlg.dialog_graph.window);
1766                 return;
1767         }
1768
1769         dialog_graph_init_window(user_data);
1770
1771 }
1772
1773 /****************************************************************************/
1774
1775 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1776 {
1777         GtkTreeIter iter;
1778         GtkTreeModel *model;
1779         GtkTreeSelection *selection;
1780         guint fnumber;
1781
1782         selection = user_data->dlg.selected_list_sel;
1783
1784         if (selection==NULL)
1785                 return;
1786         
1787         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1788                 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1789                 cf_goto_frame(&cfile, fnumber);
1790         }
1791
1792 }
1793
1794 static void draw_stat(user_data_t *user_data);
1795
1796 /****************************************************************************/
1797 /* re-dissects all packets */
1798 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1799 {
1800         GString *error_string;
1801
1802         /* remove tap listener */
1803         protect_thread_critical_region();
1804         remove_tap_listener(user_data);
1805         unprotect_thread_critical_region();
1806
1807         /* register tap listener */
1808         error_string = register_tap_listener("rtp", user_data, NULL,
1809                 rtp_reset, rtp_packet, rtp_draw);
1810         if (error_string != NULL) {
1811                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1812                         g_string_free(error_string, TRUE);
1813                 return;
1814         }
1815
1816         /* retap all packets */
1817         cf_retap_packets(&cfile, FALSE);
1818
1819         /* draw statistics info */
1820         draw_stat(user_data);
1821
1822 }
1823
1824 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1825 {
1826         GtkTreeIter iter;
1827         GtkTreeModel *model;
1828         gchar *text;
1829         GtkTreeSelection *selection;
1830         GtkTreePath *path;
1831
1832         selection = user_data->dlg.selected_list_sel;
1833
1834         if (selection==NULL)
1835                 return;
1836
1837 try_again:
1838         if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1839                 while (gtk_tree_model_iter_next (model,&iter)) {
1840                         gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1841                         if (strcmp(text, OK_TEXT) != 0) {
1842                                 gtk_tree_selection_select_iter (selection, &iter);
1843                                 path = gtk_tree_model_get_path(model, &iter);
1844                                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1845                                                 path,
1846                                                 NULL, FALSE, 0, 0);
1847                                 gtk_tree_path_free(path);
1848                                 g_free (text);
1849                                 return;
1850                         }
1851                         g_free (text);
1852                 }
1853                 /* wrap around */
1854                 if (user_data->dlg.number_of_nok>1){
1855                         /* Get the first iter and select it before starting over */
1856                         gtk_tree_model_get_iter_first(model, &iter);
1857                         gtk_tree_selection_select_iter (selection, &iter);
1858                         goto try_again;
1859                 }
1860         }
1861 }
1862
1863
1864 /****************************************************************************/
1865 /* when we want to save the information */
1866 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1867 {
1868         gchar *g_dest;
1869         GtkWidget *rev, *forw, *both;
1870         user_data_t *user_data;
1871
1872         GtkListStore *store;
1873         GtkTreeIter iter;
1874         GtkTreeSelection *selection;
1875         GtkTreeModel *model;
1876
1877         /* To Hold data from the list row */
1878         guint                   packet;         /* Packet                               */
1879         guint                   sequence;       /* Sequence                             */
1880         gfloat                  delta;          /* Delta(ms)                    */
1881         gfloat                  jitter;         /* Jitter(ms)                   */
1882         gfloat                  ipbw;           /* IP BW(kbps)                  */
1883         gboolean                marker;         /* Marker                               */
1884         char *                  status_str;     /* Status                               */
1885         char *                  date_str;       /* Date                                 */
1886         guint                   length;         /* Length                               */
1887
1888
1889         FILE *fp;
1890         int j;
1891
1892         g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1893
1894         /* Perhaps the user specified a directory instead of a file.
1895          * Check whether they did.
1896          */
1897         if (test_for_directory(g_dest) == EISDIR) {
1898                 /* It's a directory - set the file selection box to display it. */
1899                 set_last_open_dir(g_dest);
1900                 g_free(g_dest);
1901                 file_selection_set_current_folder(fs, get_last_open_dir());
1902                 return;
1903         }
1904
1905         rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1906         forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1907         both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1908         user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1909
1910         if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1911                 fp = ws_fopen(g_dest, "w");
1912                 if (fp == NULL) {
1913                         open_failure_alert_box(g_dest, errno, TRUE);
1914                         return;
1915                 }
1916
1917                 if (GTK_TOGGLE_BUTTON(both)->active) {
1918                         fprintf(fp, "Forward\n");
1919                         if (ferror(fp)) {
1920                                 write_failure_alert_box(g_dest, errno);
1921                                 fclose(fp);
1922                                 return;
1923                         }
1924                 }
1925
1926                 for(j = 0; j < NUM_COLS; j++) {
1927                         if (j == 0) {
1928                                 fprintf(fp,"%s",titles[j]);
1929                         } else {
1930                                 fprintf(fp,",%s",titles[j]);
1931                         }
1932                 }
1933                 fprintf(fp,"\n");
1934                 if (ferror(fp)) {
1935                         write_failure_alert_box(g_dest, errno);
1936                         fclose(fp);
1937                         return;
1938                 }
1939                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1940                 store = GTK_LIST_STORE(model);
1941                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1942                          
1943                          selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1944                         
1945                          while (gtk_tree_model_iter_next (model,&iter)) {
1946                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 
1947                                          0, &packet,
1948                                          1, &sequence,
1949                                          2, &delta,
1950                                          3, &jitter,
1951                                          4, &ipbw,
1952                                          5, &marker,
1953                                          6, &status_str,
1954                                          7, &date_str,
1955                                          8, &length,
1956                                          -1);
1957                                  fprintf(fp, "%u",packet);
1958                                  fprintf(fp, ",%u", sequence);
1959                                  fprintf(fp, ",%.2f", delta);
1960                                  fprintf(fp, ",%.2f", jitter);
1961                                  fprintf(fp, ",%.2f", ipbw);
1962                                  fprintf(fp, ",%s", marker? "SET" : "");
1963                                  fprintf(fp, ",%s", status_str);
1964                                  fprintf(fp, ",%s", date_str);
1965                                  fprintf(fp, ",%u", length);
1966                                  fprintf(fp,"\n");
1967                          }
1968                          if (ferror(fp)) {
1969                                  write_failure_alert_box(g_dest, errno);
1970                                  fclose(fp);
1971                                  return;
1972                          }
1973                  }
1974
1975                 if (fclose(fp) == EOF) {
1976                         write_failure_alert_box(g_dest, errno);
1977                         return;
1978                 }
1979         }
1980
1981         if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1982
1983                 if (GTK_TOGGLE_BUTTON(both)->active) {
1984                         fp = ws_fopen(g_dest, "a");
1985                         if (fp == NULL) {
1986                                 open_failure_alert_box(g_dest, errno, TRUE);
1987                                 return;
1988                         }
1989                         fprintf(fp, "\nReverse\n");
1990                         if (ferror(fp)) {
1991                                 write_failure_alert_box(g_dest, errno);
1992                                 fclose(fp);
1993                                 return;
1994                         }
1995                 } else {
1996                         fp = ws_fopen(g_dest, "w");
1997                         if (fp == NULL) {
1998                                 open_failure_alert_box(g_dest, errno, TRUE);
1999                                 return;
2000                         }
2001                 }
2002                 for(j = 0; j < NUM_COLS; j++) {
2003                         if (j == 0) {
2004                                 fprintf(fp,"%s",titles[j]);
2005                         } else {
2006                                 fprintf(fp,",%s",titles[j]);
2007                         }
2008                 }
2009                 fprintf(fp,"\n");
2010                 if (ferror(fp)) {
2011                         write_failure_alert_box(g_dest, errno);
2012                         fclose(fp);
2013                         return;
2014                 }
2015                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2016                 store = GTK_LIST_STORE(model);
2017                 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2018                          
2019                          selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_rev));
2020                         
2021                          while (gtk_tree_model_iter_next (model,&iter)) {
2022                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 
2023                                          0, &packet,
2024                                          1, &sequence,
2025                                          2, &delta,
2026                                          3, &jitter,
2027                                          4, &ipbw,
2028                                          5, &marker,
2029                                          6, &status_str,
2030                                          7, &date_str,
2031                                          8, &length,
2032                                          -1);
2033                                  fprintf(fp, "%u",packet);
2034                                  fprintf(fp, ",%u", sequence);
2035                                  fprintf(fp, ",%.2f", delta);
2036                                  fprintf(fp, ",%.2f", jitter);
2037                                  fprintf(fp, ",%.2f", ipbw);
2038                                  fprintf(fp, ",%s", marker? "SET" : "");
2039                                  fprintf(fp, ",%s", status_str);
2040                                  fprintf(fp, ",%s", date_str);
2041                                  fprintf(fp, ",%u", length);
2042                                  fprintf(fp,"\n");
2043                          }
2044                          if (ferror(fp)) {
2045                                  write_failure_alert_box(g_dest, errno);
2046                                  fclose(fp);
2047                                  return;
2048                          }
2049                  }
2050                 if (fclose(fp) == EOF) {
2051                         write_failure_alert_box(g_dest, errno);
2052                         return;
2053                 }
2054         }
2055
2056         window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2057 }
2058
2059 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2060 {
2061         user_data->dlg.save_csv_as_w = NULL;
2062 }
2063
2064 /* when the user wants to save the csv information in a file */
2065 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2066 {
2067         GtkWidget *vertb;
2068         GtkWidget *table1;
2069         GtkWidget *label_format;
2070         GtkWidget *channels_label;
2071         GSList *channels_group = NULL;
2072         GtkWidget *forward_rb;
2073         GtkWidget *reversed_rb;
2074         GtkWidget *both_rb;
2075
2076         if (user_data->dlg.save_csv_as_w != NULL) {
2077                 /* There's already a Save CSV info dialog box; reactivate it. */
2078                 reactivate_window(user_data->dlg.save_csv_as_w);
2079                 return;
2080         }
2081
2082         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,
2083                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2084                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2085                                     NULL);
2086
2087         /* Container for each row of widgets */
2088         vertb = gtk_vbox_new(FALSE, 0);
2089         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2090         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2091         gtk_widget_show (vertb);
2092
2093         table1 = gtk_table_new (2, 4, FALSE);
2094         gtk_widget_show (table1);
2095         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2096         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2097         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2098
2099         label_format = gtk_label_new ("Format: Comma Separated Values");
2100         gtk_widget_show (label_format);
2101         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2102                 (GtkAttachOptions) (GTK_FILL),
2103                 (GtkAttachOptions) (0), 0, 0);
2104
2105
2106         channels_label = gtk_label_new ("Channels:");
2107         gtk_widget_show (channels_label);
2108         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2109                 (GtkAttachOptions) (GTK_FILL),
2110                 (GtkAttachOptions) (0), 0, 0);
2111         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2112
2113         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
2114         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2115         gtk_widget_show (forward_rb);
2116         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2117                 (GtkAttachOptions) (GTK_FILL),
2118                 (GtkAttachOptions) (0), 0, 0);
2119
2120         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2121         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2122         gtk_widget_show (reversed_rb);
2123         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2124                 (GtkAttachOptions) (GTK_FILL),
2125                 (GtkAttachOptions) (0), 0, 0);
2126
2127         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2128         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2129         gtk_widget_show (both_rb);
2130         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2131                 (GtkAttachOptions) (GTK_FILL),
2132                 (GtkAttachOptions) (0), 0, 0);
2133
2134         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2135
2136         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2137         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2138         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2139         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2140
2141         g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event", 
2142                 G_CALLBACK(window_delete_event_cb), NULL);
2143         g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2144                 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2145
2146         gtk_widget_show(user_data->dlg.save_csv_as_w);
2147         window_present(user_data->dlg.save_csv_as_w);
2148
2149         if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT){
2150                 save_csv_as_ok_cb(user_data->dlg.save_csv_as_w, user_data->dlg.save_csv_as_w);
2151         }else{
2152                 window_destroy(user_data->dlg.save_csv_as_w);
2153         }
2154
2155 }
2156
2157
2158 /****************************************************************************/
2159 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2160 {
2161         /* Note that we no longer have a Save voice info dialog box. */
2162         user_data->dlg.save_voice_as_w = NULL;
2163 }
2164
2165 /****************************************************************************/
2166 /* here we save it into a file that user specified */
2167 /* XXX what about endians here? could go something wrong? */
2168 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2169 {
2170         FILE *to_stream, *forw_stream, *rev_stream;
2171         size_t fwritten, rwritten;
2172         int f_rawvalue, r_rawvalue, rawvalue;
2173         gint16 sample;
2174         gchar pd[4];
2175         guint32 f_write_silence = 0;
2176         guint32 r_write_silence = 0;
2177         progdlg_t *progbar;
2178         guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2179         gboolean stop_flag = FALSE;
2180         size_t nchars;
2181
2182         forw_stream = ws_fopen(user_data->f_tempname, "rb");
2183         if (forw_stream == NULL)
2184                 return FALSE;
2185         rev_stream = ws_fopen(user_data->r_tempname, "rb");
2186         if (rev_stream == NULL) {
2187                 fclose(forw_stream);
2188                 return FALSE;
2189         }
2190
2191         /* open file for saving */
2192         to_stream = ws_fopen(dest, "wb");
2193         if (to_stream == NULL) {
2194                 fclose(forw_stream);
2195                 fclose(rev_stream);
2196                 return FALSE;
2197         }
2198
2199         progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2200
2201         if      (format == SAVE_AU_FORMAT) /* au format */
2202         {
2203                 /* First we write the .au header. XXX Hope this is endian independant */
2204                 /* the magic word 0x2e736e64 == .snd */
2205                 phtonl(pd, 0x2e736e64);
2206                 nchars=fwrite(pd, 1, 4, to_stream);
2207                 /* header offset == 24 bytes */
2208                 phtonl(pd, 24);
2209                 nchars=fwrite(pd, 1, 4, to_stream);
2210                 /* total length; it is permitted to set this to 0xffffffff */
2211                 phtonl(pd, -1);
2212                 nchars=fwrite(pd, 1, 4, to_stream);
2213                 /* encoding format == 16-bit linear PCM */
2214                 phtonl(pd, 3);
2215                 nchars=fwrite(pd, 1, 4, to_stream);
2216                 /* sample rate == 8000 Hz */
2217                 phtonl(pd, 8000);
2218                 nchars=fwrite(pd, 1, 4, to_stream);
2219                 /* channels == 1 */
2220                 phtonl(pd, 1);
2221                 nchars=fwrite(pd, 1, 4, to_stream);
2222
2223
2224                 switch (channels) {
2225                         /* only forward direction */
2226                         case SAVE_FORWARD_DIRECTION_MASK: {
2227                                 progbar_count = user_data->forward.saveinfo.count;
2228                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2229                                 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2230                                         if(stop_flag)
2231                                                 break;
2232                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2233                                                 update_progress_dlg(progbar,
2234                                                         (gfloat) count/progbar_count, "Saving");
2235                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2236                                         }
2237                                         count++;
2238
2239                                         if (user_data->forward.statinfo.pt == PT_PCMU){
2240                                                 sample = ulaw2linear((unsigned char)f_rawvalue);
2241                                                 phtons(pd, sample);
2242                                         }
2243                                         else if(user_data->forward.statinfo.pt == PT_PCMA){
2244                                                 sample = alaw2linear((unsigned char)f_rawvalue);
2245                                                 phtons(pd, sample);
2246                                         }
2247                                         else{
2248                                                 fclose(forw_stream);
2249                                                 fclose(rev_stream);
2250                                                 fclose(to_stream);
2251                                                 destroy_progress_dlg(progbar);
2252                                                 return FALSE;
2253                                         }
2254
2255                                         fwritten = fwrite(pd, 1, 2, to_stream);
2256                                         if (fwritten < 2) {
2257                                                 fclose(forw_stream);
2258                                                 fclose(rev_stream);
2259                                                 fclose(to_stream);
2260                                                 destroy_progress_dlg(progbar);
2261                                                 return FALSE;
2262                                         }
2263                                 }
2264                                 break;
2265                         }
2266                         /* only reversed direction */
2267                         case SAVE_REVERSE_DIRECTION_MASK: {
2268                                 progbar_count = user_data->reversed.saveinfo.count;
2269                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2270                                 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2271                                         if(stop_flag)
2272                                                 break;
2273                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2274                                                 update_progress_dlg(progbar,
2275                                                         (gfloat) count/progbar_count, "Saving");
2276                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2277                                         }
2278                                         count++;
2279
2280                                         if (user_data->reversed.statinfo.pt == PT_PCMU){
2281                                                 sample = ulaw2linear((unsigned char)r_rawvalue);
2282                                                 phtons(pd, sample);
2283                                         }
2284                                         else if(user_data->reversed.statinfo.pt == PT_PCMA){
2285                                                 sample = alaw2linear((unsigned char)r_rawvalue);
2286                                                 phtons(pd, sample);
2287                                         }
2288                                         else{
2289                                                 fclose(forw_stream);
2290                                                 fclose(rev_stream);
2291                                                 fclose(to_stream);
2292                                                 destroy_progress_dlg(progbar);
2293                                                 return FALSE;
2294                                         }
2295
2296                                         rwritten = fwrite(pd, 1, 2, to_stream);
2297                                         if (rwritten < 2) {
2298                                                 fclose(forw_stream);
2299                                                 fclose(rev_stream);
2300                                                 fclose(to_stream);
2301                                                 destroy_progress_dlg(progbar);
2302                                                 return FALSE;
2303                                         }
2304                                 }
2305                                 break;
2306                         }
2307                         /* both directions */
2308                         case SAVE_BOTH_DIRECTION_MASK: {
2309                                 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2310                                                 (progbar_count = user_data->forward.saveinfo.count) :
2311                                                         (progbar_count = user_data->reversed.saveinfo.count);
2312                                 progbar_quantum = progbar_count/100;
2313                                 /* since conversation in one way can start later than in the other one,
2314                                  * we have to write some silence information for one channel */
2315                                 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2316                                         f_write_silence = (guint32)
2317                                                 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2318                                 }
2319                                 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2320                                         r_write_silence = (guint32)
2321                                                 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2322                                 }
2323                                 for(;;) {
2324                                         if(stop_flag)
2325                                                 break;
2326                                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2327                                                 update_progress_dlg(progbar,
2328                                                         (gfloat) count/progbar_count, "Saving");
2329                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2330                                         }
2331                                         count++;
2332                                         if(f_write_silence > 0) {
2333                                                 r_rawvalue = getc(rev_stream);
2334                                                 switch (user_data->forward.statinfo.reg_pt) {
2335                                                 case PT_PCMU:
2336                                                         f_rawvalue = SILENCE_PCMU;
2337                                                         break;
2338                                                 case PT_PCMA:
2339                                                         f_rawvalue = SILENCE_PCMA;
2340                                                         break;
2341                                                 default:
2342                                                         f_rawvalue = 0;
2343                                                         break;
2344                                                 }
2345                                                 f_write_silence--;
2346                                         }
2347                                         else if(r_write_silence > 0) {
2348                                                 f_rawvalue = getc(forw_stream);
2349                                                 switch (user_data->reversed.statinfo.reg_pt) {
2350                                                 case PT_PCMU:
2351                                                         r_rawvalue = SILENCE_PCMU;
2352                                                         break;
2353                                                 case PT_PCMA:
2354                                                         r_rawvalue = SILENCE_PCMA;
2355                                                         break;
2356                                                 default:
2357                                                         r_rawvalue = 0;
2358                                                         break;
2359                                                 }
2360                                                 r_write_silence--;
2361                                         }
2362                                         else {
2363                                                 f_rawvalue = getc(forw_stream);
2364                                                 r_rawvalue = getc(rev_stream);
2365                                         }
2366                                         if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2367                                                 break;
2368                                         if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2369                                                 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2370                                                 phtons(pd, sample);
2371                                         }
2372                                         else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2373                                                 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2374                                                 phtons(pd, sample);
2375                                         }
2376                                         else
2377                                         {
2378                                                 fclose(forw_stream);
2379                                                 fclose(rev_stream);
2380                                                 fclose(to_stream);
2381                                                 destroy_progress_dlg(progbar);
2382                                                 return FALSE;
2383                                         }
2384
2385
2386                                         rwritten = fwrite(pd, 1, 2, to_stream);
2387                                         if (rwritten < 2) {
2388                                                 fclose(forw_stream);
2389                                                 fclose(rev_stream);
2390                                                 fclose(to_stream);
2391                                                 destroy_progress_dlg(progbar);
2392                                                 return FALSE;
2393                                         }
2394                                 }
2395                         }
2396                 }
2397         }
2398         else if (format == SAVE_RAW_FORMAT)     /* raw format */
2399         {
2400                 FILE *stream;
2401                 switch (channels) {
2402                         /* only forward direction */
2403                         case SAVE_FORWARD_DIRECTION_MASK: {
2404                                 progbar_count = user_data->forward.saveinfo.count;
2405                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2406                                 stream = forw_stream;
2407                                 break;
2408                         }
2409                         /* only reversed direction */
2410                         case SAVE_REVERSE_DIRECTION_MASK: {
2411                                 progbar_count = user_data->reversed.saveinfo.count;
2412                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2413                                 stream = rev_stream;
2414                                 break;
2415                         }
2416                         default: {
2417                                 fclose(forw_stream);
2418                                 fclose(rev_stream);
2419                                 fclose(to_stream);
2420                                 destroy_progress_dlg(progbar);
2421                                 return FALSE;
2422                         }
2423                 }
2424
2425
2426
2427                 /* XXX how do you just copy the file? */
2428                 while ((rawvalue = getc(stream)) != EOF) {
2429                         if(stop_flag)
2430                                 break;
2431                         if((count > progbar_nextstep) && (count <= progbar_count)) {
2432                                 update_progress_dlg(progbar,
2433                                         (gfloat) count/progbar_count, "Saving");
2434                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2435                         }
2436                         count++;
2437
2438                         if (putc(rawvalue, to_stream) == EOF) {
2439                                 fclose(forw_stream);
2440                                 fclose(rev_stream);
2441                                 fclose(to_stream);
2442                                 destroy_progress_dlg(progbar);
2443                                 return FALSE;
2444                         }
2445                 }
2446         }
2447
2448         destroy_progress_dlg(progbar);
2449         fclose(forw_stream);
2450         fclose(rev_stream);
2451         fclose(to_stream);
2452         return TRUE;
2453 }
2454
2455
2456 /****************************************************************************/
2457 /* the user wants to save in a file */
2458 /* XXX support for different formats is currently commented out */
2459 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2460 {
2461         gchar *g_dest;
2462         /*GtkWidget *wav, *sw;*/
2463         GtkWidget *au, *raw;
2464         GtkWidget *rev, *forw, *both;
2465         user_data_t *user_data;
2466         gint channels , format;
2467
2468         g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
2469
2470         /* Perhaps the user specified a directory instead of a file.
2471         Check whether they did. */
2472         if (test_for_directory(g_dest) == EISDIR) {
2473                 /* It's a directory - set the file selection box to display it. */
2474                 set_last_open_dir(g_dest);
2475                 g_free(g_dest);
2476                 file_selection_set_current_folder(fs, get_last_open_dir());
2477                 return;
2478         }
2479
2480         /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2481         sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2482         au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2483         raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2484         rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2485         forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2486         both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2487         user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2488
2489         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2490         * we don't support that codec. So we pop up a warning. Maybe it would be better to
2491         * disable the ok button or disable the buttons for direction if only one is not ok. The
2492         * problem is if we open the save voice dialog and then click the refresh button and maybe
2493         * the state changes, so we can't save anymore. In this case we should be able to update
2494         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2495         */
2496
2497         /* we can not save in both directions */
2498         if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2499                 /* there are many combinations here, we just exit when first matches */
2500                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2501                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2502                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2503                         "Can't save in a file: Unsupported codec!");
2504                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2505                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2506                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2507                         "Can't save in a file: Wrong length of captured packets!");
2508                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2509                         (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2510                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2511                         "Can't save in a file: RTP data with padding!");
2512                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2513                         (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2514                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2515                         "Can't save in a file: Not all data in all packets was captured!");
2516                 else
2517                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2518                         "Can't save in a file: File I/O problem!");
2519                 return;
2520         }
2521         /* we can not save forward direction */
2522         else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2523                 (GTK_TOGGLE_BUTTON (both)->active))) {
2524                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2525                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2526                         "Can't save forward direction in a file: Unsupported codec!");
2527                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2528                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2529                         "Can't save forward direction in a file: Wrong length of captured packets!");
2530                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2531                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2532                         "Can't save forward direction in a file: RTP data with padding!");
2533                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2534                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2535                         "Can't save forward direction in a file: Not all data in all packets was captured!");
2536                 else
2537                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2538                         "Can't save forward direction in a file: File I/O problem!");
2539                 return;
2540         }
2541         /* we can not save reversed direction */
2542         else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2543                 (GTK_TOGGLE_BUTTON (both)->active))) {
2544                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2545                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2546                         "Can't save reversed direction in a file: Unsupported codec!");
2547                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2548                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2549                         "Can't save reversed direction in a file: Wrong length of captured packets!");
2550                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2551                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2552                         "Can't save reversed direction in a file: RTP data with padding!");
2553                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2554                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2555                         "Can't save reversed direction in a file: Not all data in all packets was captured!");
2556                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2557                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2558                         "Can't save reversed direction in a file: No RTP data!");
2559                 else
2560                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2561                         "Can't save reversed direction in a file: File I/O problem!");
2562                 return;
2563         }
2564
2565         /*if (GTK_TOGGLE_BUTTON (wav)->active)
2566         format = SAVE_WAV_FORMAT;
2567         else */if (GTK_TOGGLE_BUTTON (au)->active)
2568         format = SAVE_AU_FORMAT;
2569         /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2570         format = SAVE_SW_FORMAT;*/
2571         else if (GTK_TOGGLE_BUTTON (raw)->active)
2572         format = SAVE_RAW_FORMAT;
2573         else
2574         format = SAVE_NONE_FORMAT;
2575
2576         if (GTK_TOGGLE_BUTTON (rev)->active)
2577                 channels = SAVE_REVERSE_DIRECTION_MASK;
2578         else if (GTK_TOGGLE_BUTTON (both)->active)
2579                 channels = SAVE_BOTH_DIRECTION_MASK;
2580         else
2581                 channels = SAVE_FORWARD_DIRECTION_MASK;
2582
2583         /* direction/format validity*/
2584         if (format == SAVE_AU_FORMAT)
2585         {
2586                 /* make sure streams are alaw/ulaw */
2587                 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2588                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2589                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2590                         return;
2591                 }
2592                 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2593                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2594                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2595                         return;
2596                 }
2597                 /* make sure pt's don't differ */
2598                 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2599                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2600                                 "Can't save in a file: Forward and reverse direction differ in type");
2601                         return;
2602                 }
2603         }
2604         else if (format == SAVE_RAW_FORMAT)
2605         {
2606                 /* can't save raw in both directions */
2607                 if (channels == SAVE_BOTH_DIRECTION_MASK){
2608                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2609                                 "Can't save in a file: Unable to save raw data in both directions");
2610                         return;
2611                 }
2612         }
2613         else
2614         {
2615                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2616                         "Can't save in a file: Invalid save format");
2617                 return;
2618         }
2619
2620         if(!copy_file(g_dest, channels, format, user_data)) {
2621                 /* XXX - report the error type! */
2622                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2623                         "An error occurred while saving voice in a file!");
2624                 return;
2625         }
2626
2627         window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2628 }
2629
2630 /****************************************************************************/
2631 /* when the user wants to save the voice information in a file */
2632 /* XXX support for different formats is currently commented out */
2633 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2634 {
2635         GtkWidget *vertb;
2636         GtkWidget *table1;
2637         GtkWidget *label_format;
2638         GtkWidget *channels_label;
2639         GSList *format_group = NULL;
2640         GSList *channels_group = NULL;
2641         GtkWidget *forward_rb;
2642         GtkWidget *reversed_rb;
2643         GtkWidget *both_rb;
2644         /*GtkWidget *wav_rb;  GtkWidget *sw_rb;*/
2645         GtkWidget *au_rb;
2646         GtkWidget *raw_rb;
2647
2648         /* if we can't save in a file: wrong codec, cut packets or other errors */
2649         /* shold the error arise here or later when you click ok button ?
2650         * if we do it here, then we must disable the refresh button, so we don't do it here */
2651
2652         if (user_data->dlg.save_voice_as_w != NULL) {
2653                 /* There's already a Save voice info dialog box; reactivate it. */
2654                 reactivate_window(user_data->dlg.save_voice_as_w);
2655                 return;
2656         }
2657
2658     /* XXX - use file_selection from dlg_utils instead! */
2659         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,
2660                                     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2661                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2662                                     NULL);
2663
2664
2665         /* Container for each row of widgets */
2666         vertb = gtk_vbox_new(FALSE, 0);
2667         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2668         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2669         gtk_widget_show (vertb);
2670
2671         table1 = gtk_table_new (2, 4, FALSE);
2672         gtk_widget_show (table1);
2673         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2674         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2675         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2676
2677         /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2678         gtk_widget_show (label_format);
2679         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2680                 (GtkAttachOptions) (GTK_FILL),
2681                 (GtkAttachOptions) (0), 0, 0);*/
2682
2683         label_format = gtk_label_new ("Format: ");
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         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
2690
2691         raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2692         format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2693         gtk_widget_show (raw_rb);
2694         gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2695         (GtkAttachOptions) (GTK_FILL),
2696         (GtkAttachOptions) (0), 0, 0);
2697
2698
2699         au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2700         format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2701         gtk_widget_show (au_rb);
2702         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2703         (GtkAttachOptions) (GTK_FILL),
2704         (GtkAttachOptions) (0), 0, 0);
2705
2706         /* we support .au - ulaw*/
2707         /*      wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2708         format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2709         gtk_widget_show (wav_rb);
2710         gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2711         (GtkAttachOptions) (GTK_FILL),
2712         (GtkAttachOptions) (0), 0, 0);
2713
2714           sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit  ");
2715           format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2716           gtk_widget_show (sw_rb);
2717           gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2718           (GtkAttachOptions) (GTK_FILL),
2719           (GtkAttachOptions) (0), 0, 0);
2720           au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2721           format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2722           gtk_widget_show (au_rb);
2723           gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2724           (GtkAttachOptions) (GTK_FILL),
2725           (GtkAttachOptions) (0), 0, 0);
2726         */
2727
2728
2729         channels_label = gtk_label_new ("Channels:");
2730         gtk_widget_show (channels_label);
2731         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2732                 (GtkAttachOptions) (GTK_FILL),
2733                 (GtkAttachOptions) (0), 0, 0);
2734         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2735
2736         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
2737         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2738         gtk_widget_show (forward_rb);
2739         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2740                 (GtkAttachOptions) (GTK_FILL),
2741                 (GtkAttachOptions) (0), 0, 0);
2742
2743         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2744         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2745         gtk_widget_show (reversed_rb);
2746         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2747                 (GtkAttachOptions) (GTK_FILL),
2748                 (GtkAttachOptions) (0), 0, 0);
2749
2750         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2751         channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2752         gtk_widget_show (both_rb);
2753         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2754                 (GtkAttachOptions) (GTK_FILL),
2755                 (GtkAttachOptions) (0), 0, 0);
2756
2757         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2758
2759         /* if one direction is nok we don't allow saving
2760         XXX this is not ok since the user can click the refresh button and cause changes
2761         but we can not update this window. So we move all the decision on the time the ok
2762         button is clicked
2763         if (user_data->forward.saved == FALSE) {
2764         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2765         gtk_widget_set_sensitive(forward_rb, FALSE);
2766         gtk_widget_set_sensitive(both_rb, FALSE);
2767         }
2768         else if (user_data->reversed.saved == FALSE) {
2769         gtk_widget_set_sensitive(reversed_rb, FALSE);
2770         gtk_widget_set_sensitive(both_rb, FALSE);
2771         }
2772         */
2773
2774         /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2775         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2776         /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2777         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2778         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2779         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2780         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2781         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2782
2783     g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2784                         G_CALLBACK(window_delete_event_cb), NULL);
2785         g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2786                         G_CALLBACK(save_voice_as_destroy_cb), user_data);
2787
2788         gtk_widget_show(user_data->dlg.save_voice_as_w);
2789     window_present(user_data->dlg.save_voice_as_w);
2790
2791         if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT){
2792                 save_voice_as_ok_cb(user_data->dlg.save_voice_as_w, user_data->dlg.save_voice_as_w);
2793         }else{
2794                 window_destroy(user_data->dlg.save_voice_as_w);
2795         }
2796
2797 }
2798
2799
2800 /****************************************************************************/
2801 /* when we are finished with redisection, we add the label for the statistic */
2802 static void draw_stat(user_data_t *user_data)
2803 {
2804         gchar label_max[200];
2805         guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2806                 - user_data->forward.statinfo.start_seq_nr + 1;
2807         guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2808                 - user_data->reversed.statinfo.start_seq_nr + 1;
2809         gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2810         gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2811         double f_perc, r_perc;
2812         if (f_expected){
2813                 f_perc = (double)(f_lost*100)/(double)f_expected;
2814         } else {
2815                 f_perc = 0;
2816         }
2817         if (r_expected){
2818                 r_perc = (double)(r_lost*100)/(double)r_expected;
2819         } else {
2820                 r_perc = 0;
2821         }
2822
2823         g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2824                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
2825                 "   Sequence errors = %u",
2826                 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2827                 user_data->forward.statinfo.total_nr,
2828                 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2829
2830         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2831
2832         g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2833                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
2834                 "   Sequence errors = %u",
2835                 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2836                 user_data->reversed.statinfo.total_nr,
2837                 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2838
2839         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2840
2841         return ;
2842 }
2843
2844
2845
2846 /****************************************************************************/
2847 /* append a line to list */
2848 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
2849                          double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2850                          gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2851 {
2852     GtkListStore *list_store;
2853
2854         if (strcmp(status, OK_TEXT) != 0) {
2855                 user_data->dlg.number_of_nok++;
2856         }
2857
2858     list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2859
2860         /* Creates a new row at position. iter will be changed to point to this new row. 
2861          * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2862          * The row will be filled with the values given to this function.
2863          * :
2864          * should generally be preferred when inserting rows in a sorted list store.
2865          */
2866 #if GTK_CHECK_VERSION(2,6,0)
2867         gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2868 #else
2869         gtk_list_store_append  (list_store, &user_data->dlg.iter);
2870         gtk_list_store_set  (list_store, &user_data->dlg.iter,
2871 #endif
2872                 PACKET_COLUMN, number,
2873                 SEQUENCE_COLUMN, seq_num,
2874                                 DELTA_COLUMN, delta,
2875                                 JITTER_COLUMN, jitter,
2876                                 IPBW_COLUMN, bandwidth,
2877                                 MARKER_COLUMN, marker,
2878                                 STATUS_COLUMN, (char *)status,
2879                                 DATE_COLUMN,  (char *)timeStr,
2880                                 LENGTH_COLUMN,  pkt_len,
2881                                 FOREGROUND_COLOR_COL, NULL,
2882                                 BACKGROUND_COLOR_COL, (char *)color_str,
2883                                 -1);
2884
2885         if(flags & STAT_FLAG_FIRST){
2886                 /* Set first row as active */
2887                 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2888         }
2889 }
2890
2891 /****************************************************************************
2892 * Functions needed to present values from the list
2893 */
2894
2895
2896 /* Present boolean value */
2897 void
2898 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
2899                            GtkCellRenderer   *renderer,
2900                            GtkTreeModel      *model,
2901                            GtkTreeIter       *iter,
2902                            gpointer           user_data)
2903    {
2904      gboolean  bool_val;
2905      gchar   buf[20];
2906          /* the col to get data from is in userdata */
2907          gint bool_col = GPOINTER_TO_INT(user_data);
2908
2909      gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
2910
2911          switch(bool_col){
2912                  case MARKER_COLUMN:
2913                          g_snprintf(buf, sizeof(buf), "%s", bool_val? "SET" : "");
2914                          break;
2915                  default:
2916                          g_assert_not_reached();
2917                          break;
2918          }
2919      g_object_set(renderer, "text", buf, NULL);
2920    }
2921
2922 /* Create list */
2923 static
2924 GtkWidget* create_list(user_data_t* user_data)
2925 {
2926
2927     GtkListStore *list_store;
2928     GtkWidget *list;
2929     GtkTreeViewColumn *column;
2930     GtkCellRenderer *renderer;
2931     GtkTreeSortable *sortable;
2932         GtkTreeView     *list_view;
2933         GtkTreeSelection  *selection;
2934
2935         /* Create the store */
2936     list_store = gtk_list_store_new(N_COLUMN,   /* Total number of columns XXX*/
2937                                G_TYPE_UINT,             /* Packet                               */
2938                                G_TYPE_UINT,             /* Sequence                             */
2939                                G_TYPE_FLOAT,    /* Delta(ms)                    */
2940                                G_TYPE_FLOAT,    /* Jitter(ms)                   */
2941                                G_TYPE_FLOAT,    /* IP BW(kbps)                  */
2942                                G_TYPE_BOOLEAN,  /* Marker                               */
2943                                G_TYPE_STRING,   /* Status                               */
2944                                G_TYPE_STRING,   /* Date                                 */
2945                                G_TYPE_UINT,             /* Length                               */
2946                                                            G_TYPE_STRING,   /* Foreground color         */
2947                                                            G_TYPE_STRING);  /* Background color         */
2948
2949     /* Create a view */
2950     list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2951
2952         list_view = GTK_TREE_VIEW(list);
2953         sortable = GTK_TREE_SORTABLE(list_store);
2954
2955 #if GTK_CHECK_VERSION(2,6,0)
2956         /* Speed up the list display */
2957         gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2958 #endif
2959
2960     /* Setup the sortable columns */
2961     gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2962     gtk_tree_view_set_headers_clickable(list_view, FALSE);
2963
2964     /* The view now holds a reference.  We can get rid of our own reference */
2965     g_object_unref (G_OBJECT (list_store));
2966
2967     /* 
2968          * Create the first column packet, associating the "text" attribute of the
2969      * cell_renderer to the first column of the model 
2970          */
2971     renderer = gtk_cell_renderer_text_new ();
2972     column = gtk_tree_view_column_new_with_attributes ("Packet", renderer, 
2973                 "text", PACKET_COLUMN, 
2974         "foreground", FOREGROUND_COLOR_COL,
2975         "background", BACKGROUND_COLOR_COL,
2976                 NULL);
2977     gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2978     gtk_tree_view_column_set_resizable(column, TRUE);
2979     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2980     gtk_tree_view_column_set_min_width(column, 100);
2981
2982         /* Add the column to the view. */
2983     gtk_tree_view_append_column (list_view, column);
2984
2985     /* Second column.. Sequence. */
2986     renderer = gtk_cell_renderer_text_new ();
2987     column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer, 
2988                 "text", SEQUENCE_COLUMN,
2989         "foreground", FOREGROUND_COLOR_COL,
2990         "background", BACKGROUND_COLOR_COL,
2991                 NULL);
2992     gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
2993     gtk_tree_view_column_set_resizable(column, TRUE);
2994     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2995     gtk_tree_view_column_set_min_width(column, 100);
2996     gtk_tree_view_append_column (list_view, column);
2997
2998     /* Third column.. Delta(ms). */
2999     renderer = gtk_cell_renderer_text_new ();
3000         column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer, 
3001                 "text", DELTA_COLUMN, 
3002         "foreground", FOREGROUND_COLOR_COL,
3003         "background", BACKGROUND_COLOR_COL,
3004                 NULL);
3005         
3006         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func, 
3007                 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3008
3009     gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3010     gtk_tree_view_column_set_resizable(column, TRUE);
3011     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3012     gtk_tree_view_column_set_min_width(column, 100);
3013     gtk_tree_view_append_column (list_view, column);
3014
3015     /* Forth column.. Jitter(ms). */
3016     renderer = gtk_cell_renderer_text_new ();
3017     column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer, 
3018                 "text", JITTER_COLUMN, 
3019         "foreground", FOREGROUND_COLOR_COL,
3020         "background", BACKGROUND_COLOR_COL,
3021                 NULL);
3022
3023         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func, 
3024                 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3025
3026     gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3027     gtk_tree_view_column_set_resizable(column, TRUE);
3028     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3029     gtk_tree_view_column_set_min_width(column, 100);
3030     gtk_tree_view_append_column (list_view, column);
3031
3032     /* Fifth column.. IP BW(kbps). */
3033     renderer = gtk_cell_renderer_text_new ();
3034     column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer, 
3035                 "text", IPBW_COLUMN, 
3036         "foreground", FOREGROUND_COLOR_COL,
3037         "background", BACKGROUND_COLOR_COL,
3038                 NULL);
3039
3040         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func, 
3041                 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3042
3043     gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3044     gtk_tree_view_column_set_resizable(column, TRUE);
3045     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3046     gtk_tree_view_column_set_min_width(column, 100);
3047     gtk_tree_view_append_column (list_view, column);
3048
3049     /* Sixth column.. Marker. */
3050     renderer = gtk_cell_renderer_text_new ();
3051     column = gtk_tree_view_column_new_with_attributes ("Marker", renderer, 
3052                 "text", MARKER_COLUMN, 
3053         "foreground", FOREGROUND_COLOR_COL,
3054         "background", BACKGROUND_COLOR_COL,
3055                 NULL);
3056
3057         gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func, 
3058                 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3059
3060     gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3061     gtk_tree_view_column_set_resizable(column, TRUE);
3062     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3063     gtk_tree_view_column_set_min_width(column, 75);
3064     gtk_tree_view_append_column (list_view, column);
3065
3066          /* Seventh column.. Status. */
3067     renderer = gtk_cell_renderer_text_new ();
3068     column = gtk_tree_view_column_new_with_attributes ( "Status", renderer, 
3069                 "text", STATUS_COLUMN,
3070         "foreground", FOREGROUND_COLOR_COL,
3071         "background", BACKGROUND_COLOR_COL,
3072                 NULL);
3073     gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3074     gtk_tree_view_column_set_resizable(column, TRUE);
3075     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3076     gtk_tree_view_column_set_min_width(column, 100);
3077     gtk_tree_view_append_column (list_view, column);
3078
3079     /* Now enable the sorting of each column */
3080     gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3081     gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3082
3083         /* Setup the selection handler */
3084         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3085         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3086
3087         g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3088                   G_CALLBACK (on_list_select_row),
3089                   user_data);
3090         return list;
3091 }
3092
3093 /****************************************************************************/
3094 /* Create the dialog box with all widgets */
3095 static void create_rtp_dialog(user_data_t* user_data)
3096 {
3097         GtkWidget *window = NULL;
3098         GtkWidget *list_fwd;
3099         GtkWidget *list_rev;
3100         GtkWidget *label_stats_fwd;
3101         GtkWidget *label_stats_rev;
3102         GtkWidget *notebook;
3103
3104         GtkWidget *main_vb, *page, *page_r;
3105         GtkWidget *label;
3106         GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3107         GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3108 #ifdef USE_CONVERSATION_GRAPH
3109         GtkWidget *graph_bt;
3110 #endif
3111         GtkWidget *graph_bt;
3112         gchar label_forward[150];
3113         gchar label_forward_tree[150];
3114         gchar label_reverse[150];
3115
3116         gchar str_ip_src[16];
3117         gchar str_ip_dst[16];
3118
3119         window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
3120         gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3121
3122         /* Container for each row of widgets */
3123         main_vb = gtk_vbox_new(FALSE, 2);
3124         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3125         gtk_container_add(GTK_CONTAINER(window), main_vb);
3126         gtk_widget_show(main_vb);
3127
3128         /* Notebooks... */
3129         g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3130         g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3131
3132         g_snprintf(label_forward, 149,
3133                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X",
3134                 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3135
3136         g_snprintf(label_forward_tree, 149,
3137                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X",
3138                 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3139
3140
3141         g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3142         g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3143
3144         g_snprintf(label_reverse, 149,
3145                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X",
3146                 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3147
3148         /* Start a notebook for flipping between sets of changes */
3149         notebook = gtk_notebook_new();
3150         gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3151         g_object_set_data(G_OBJECT(window), "notebook", notebook);
3152
3153         user_data->dlg.notebook_signal_id =
3154         g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3155
3156         /* page for forward connection */
3157         page = gtk_vbox_new(FALSE, 8);
3158         gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3159
3160         /* direction label */
3161         label = gtk_label_new(label_forward);
3162         gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3163
3164         /* place for some statistics */
3165         label_stats_fwd = gtk_label_new("\n");
3166         gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3167
3168         /* scrolled window */
3169         scrolled_window = scrolled_window_new(NULL, NULL);
3170
3171         /* packet list */
3172         list_fwd = create_list(user_data);
3173         gtk_widget_show(list_fwd);
3174         gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3175         gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3176         gtk_widget_show(scrolled_window);
3177
3178         /* tab */
3179         label = gtk_label_new("  Forward Direction  ");
3180         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3181
3182         /* same page for reversed connection */
3183         page_r = gtk_vbox_new(FALSE, 8);
3184         gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3185         label = gtk_label_new(label_reverse);
3186         gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3187         label_stats_rev = gtk_label_new("\n");
3188         gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3189
3190         scrolled_window_r = scrolled_window_new(NULL, NULL);
3191
3192         list_rev = create_list(user_data);
3193         gtk_widget_show(list_rev);
3194         gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3195         gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3196         gtk_widget_show(scrolled_window_r);
3197
3198         label = gtk_label_new("  Reversed Direction  ");
3199         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3200
3201         /* page for help&about or future
3202         page_help = gtk_hbox_new(FALSE, 5);
3203         label = gtk_label_new("     Future    ");
3204         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3205         frame = gtk_frame_new("");
3206         text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3207         gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3208         gtk_container_add(GTK_CONTAINER(frame), text);
3209         gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3210         gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3211         */
3212
3213         /* show all notebooks */
3214         gtk_widget_show_all(notebook);
3215
3216         /* buttons */
3217         box4 = gtk_hbutton_box_new();
3218         gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3219         gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3220         gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3221         gtk_box_set_spacing(GTK_BOX (box4), 0);
3222         gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3223         gtk_widget_show(box4);
3224
3225         voice_bt = gtk_button_new_with_label("Save payload...");
3226         gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3227         gtk_widget_show(voice_bt);
3228         g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3229
3230         csv_bt = gtk_button_new_with_label("Save as CSV...");
3231         gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3232         gtk_widget_show(csv_bt);
3233         g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3234
3235         refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3236         gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3237         gtk_widget_show(refresh_bt);
3238         g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3239
3240         goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3241         gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3242         gtk_widget_show(goto_bt);
3243         g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3244
3245     graph_bt = gtk_button_new_with_label("Graph");
3246         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3247         gtk_widget_show(graph_bt);
3248         g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3249
3250
3251 #ifdef USE_CONVERSATION_GRAPH
3252         graph_bt = gtk_button_new_with_label("Graph");
3253         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3254         gtk_widget_show(graph_bt);
3255         g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3256 #endif
3257
3258         next_bt = gtk_button_new_with_label("Next non-Ok");
3259         gtk_container_add(GTK_CONTAINER(box4), next_bt);
3260         gtk_widget_show(next_bt);
3261         g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3262
3263         close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3264         gtk_container_add(GTK_CONTAINER(box4), close_bt);
3265     GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3266         gtk_widget_show(close_bt);
3267     window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3268
3269     g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3270         g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3271
3272     gtk_widget_show(window);
3273     window_present(window);
3274
3275
3276         /* some widget references need to be saved for outside use */
3277         user_data->dlg.window = window;
3278         user_data->dlg.list_fwd = list_fwd;
3279         user_data->dlg.list_rev = list_rev;
3280         user_data->dlg.label_stats_fwd = label_stats_fwd;
3281         user_data->dlg.label_stats_rev = label_stats_rev;
3282         user_data->dlg.notebook = notebook;
3283         user_data->dlg.selected_list = list_fwd;
3284         user_data->dlg.number_of_nok = 0;
3285
3286         /*
3287          * select the initial row
3288          */
3289         gtk_widget_grab_focus(list_fwd);
3290
3291 }
3292
3293
3294 /****************************************************************************/
3295 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3296                                                         const gchar* proto_field, guint32* p_result)
3297 {
3298         field_info            *finfo;
3299         proto_node            *proto_sibling_node;
3300         header_field_info     *hfssrc;
3301         ipv4_addr             *ipv4;
3302
3303         finfo = PITEM_FINFO(ptree_node);
3304
3305         if (hfinformation==(finfo->hfinfo)) {
3306                 hfssrc = proto_registrar_get_byname(proto_field);
3307                 if (hfssrc == NULL)
3308                         return FALSE;
3309                 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3310                                         ptree_node=ptree_node->next) {
3311                         finfo=PITEM_FINFO(ptree_node);
3312                         if (hfssrc==finfo->hfinfo) {
3313                                 if (hfinformation->type==FT_IPv4) {
3314                                         ipv4 = fvalue_get(&finfo->value);
3315                                         *p_result = ipv4_get_net_order_addr(ipv4);
3316                                 }
3317                                 else {
3318                                         *p_result = fvalue_get_uinteger(&finfo->value);
3319                                 }
3320                                 return TRUE;
3321                         }
3322                 }
3323                 if(!ptree_node)
3324                         return FALSE;
3325         }
3326
3327         proto_sibling_node = ptree_node->next;
3328
3329         if (proto_sibling_node) {
3330                 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3331         }
3332         else
3333         return FALSE;
3334 }
3335
3336 /****************************************************************************/
3337 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3338                                                  const gchar* proto_name,
3339                                                  const gchar* proto_field,
3340                                                  guint32* p_result)
3341 {
3342         proto_node      *ptree_node;
3343         header_field_info     *hfinformation;
3344
3345         hfinformation = proto_registrar_get_byname(proto_name);
3346         if (hfinformation == NULL)
3347                 return FALSE;
3348
3349         ptree_node = ((proto_node *)protocol_tree)->first_child;
3350         if (!ptree_node)
3351                 return FALSE;
3352
3353         return process_node(ptree_node, hfinformation, proto_field, p_result);
3354 }
3355
3356
3357 /****************************************************************************/
3358 void rtp_analysis(
3359                 address *ip_src_fwd,
3360                 guint16 port_src_fwd,
3361                 address *ip_dst_fwd,
3362                 guint16 port_dst_fwd,
3363                 guint32 ssrc_fwd,
3364                 address *ip_src_rev,
3365                 guint16 port_src_rev,
3366                 address *ip_dst_rev,
3367                 guint16 port_dst_rev,
3368                 guint32 ssrc_rev
3369                 )
3370 {
3371         user_data_t *user_data;
3372         int fd;
3373         int i;
3374         static color_t col[MAX_GRAPHS] = {
3375                 {0,     0x0000, 0x0000, 0x0000},
3376                 {0,     0xffff, 0x0000, 0x0000},
3377                 {0,     0x0000, 0xffff, 0x0000},
3378                 {0,     0x0000, 0x0000, 0xffff}
3379         };
3380
3381         /* init */
3382         user_data = g_malloc(sizeof(user_data_t));
3383
3384         COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3385         user_data->port_src_fwd = port_src_fwd;
3386         COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3387         user_data->port_dst_fwd = port_dst_fwd;
3388         user_data->ssrc_fwd = ssrc_fwd;
3389         COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3390         user_data->port_src_rev = port_src_rev;
3391         COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3392         user_data->port_dst_rev = port_dst_rev;
3393         user_data->ssrc_rev = ssrc_rev;
3394
3395
3396         /* file names for storing sound data */
3397         /*XXX: check for errors*/
3398         fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3399                 "ether_rtp_f");
3400         ws_close(fd);
3401         fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3402                 "ether_rtp_r");
3403         ws_close(fd);
3404         user_data->forward.saveinfo.fp = NULL;
3405         user_data->reversed.saveinfo.fp = NULL;
3406         user_data->dlg.save_voice_as_w = NULL;
3407         user_data->dlg.save_csv_as_w = NULL;
3408     user_data->dlg.dialog_graph.window = NULL;
3409
3410 #ifdef USE_CONVERSATION_GRAPH
3411         user_data->dlg.graph_window = NULL;
3412         user_data->series_fwd.value_pairs = NULL;
3413         user_data->series_rev.value_pairs = NULL;
3414 #endif
3415
3416         /* init dialog_graph */
3417         user_data->dlg.dialog_graph.needs_redraw=TRUE;
3418         user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3419         user_data->dlg.dialog_graph.draw_area=NULL;
3420         user_data->dlg.dialog_graph.pixmap=NULL;
3421         user_data->dlg.dialog_graph.scrollbar=NULL;
3422         user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3423         user_data->dlg.dialog_graph.pixmap_width=500;
3424         user_data->dlg.dialog_graph.pixmap_height=200;
3425         user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3426         user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3427         user_data->dlg.dialog_graph.last_interval=0xffffffff;
3428         user_data->dlg.dialog_graph.max_interval=0;
3429         user_data->dlg.dialog_graph.num_items=0;
3430         user_data->dlg.dialog_graph.start_time = -1;
3431
3432         for(i=0;i<MAX_GRAPHS;i++){
3433                 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3434                 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3435                 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3436                 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3437                 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3438                 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3439                 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3440                 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3441         }
3442
3443         /* create the dialog box */
3444         create_rtp_dialog(user_data);
3445
3446         /* proceed as if the Refresh button would have been pressed */
3447         on_refresh_bt_clicked(NULL, user_data);
3448 }
3449
3450 /****************************************************************************/
3451 /* entry point from main menu */
3452 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3453 {
3454         address ip_src_fwd;
3455         guint16 port_src_fwd;
3456         address ip_dst_fwd;
3457         guint16 port_dst_fwd;
3458         guint32 ssrc_fwd = 0;
3459         address ip_src_rev;
3460         guint16 port_src_rev;
3461         address ip_dst_rev;
3462         guint16 port_dst_rev;
3463         guint32 ssrc_rev = 0;
3464         unsigned int version_fwd;
3465
3466         gchar filter_text[256];
3467         dfilter_t *sfcode;
3468         capture_file *cf;
3469         epan_dissect_t *edt;
3470         gint err;
3471         gchar *err_info;
3472         gboolean frame_matched;
3473         frame_data *fdata;
3474         GList *strinfo_list;
3475         GList *filtered_list = NULL;
3476         rtp_stream_info_t *strinfo;
3477         guint nfound;
3478
3479         /* Try to compile the filter. */
3480         g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",256);
3481         if (!dfilter_compile(filter_text, &sfcode)) {
3482                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3483                 return;
3484         }
3485         /* we load the current file into cf variable */
3486         cf = &cfile;
3487         fdata = cf->current_frame;
3488
3489         /* we are on the selected frame now */
3490         if (fdata == NULL)
3491                 return; /* if we exit here it's an error */
3492
3493         /* dissect the current frame */
3494         if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3495             cf->pd, fdata->cap_len, &err, &err_info)) {
3496                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3497                         cf_read_error_message(err, err_info), cf->filename);
3498                 return;
3499         }
3500         edt = epan_dissect_new(TRUE, FALSE);
3501         epan_dissect_prime_dfilter(edt, sfcode);
3502         epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3503         frame_matched = dfilter_apply_edt(sfcode, edt);
3504
3505         /* if it is not an rtp frame, show the rtpstream dialog */
3506         frame_matched = dfilter_apply_edt(sfcode, edt);
3507         if (frame_matched != 1) {
3508                 epan_dissect_free(edt);
3509                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3510                     "You didn't choose a RTP packet!");
3511                 return;
3512         }
3513
3514         /* ok, it is a RTP frame, so let's get the ip and port values */
3515         COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3516         COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3517         port_src_fwd = edt->pi.srcport;
3518         port_dst_fwd = edt->pi.destport;
3519
3520         /* assume the inverse ip/port combination for the reverse direction */
3521         COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3522         COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3523         port_src_rev = edt->pi.destport;
3524         port_dst_rev = edt->pi.srcport;
3525
3526         /* check if it is RTP Version 2 */
3527         if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3528                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3529                     "RTP Version != 2 isn't supported!");
3530                 return;
3531         }
3532
3533         /* now we need the SSRC value of the current frame */
3534         if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3535                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3536                     "SSRC value couldn't be found!");
3537                 return;
3538         }
3539
3540         /* Scan for rtpstream */
3541         rtpstream_scan();
3542         /* search for reversed direction in the global rtp streams list */
3543         nfound = 0;
3544         strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3545         while (strinfo_list)
3546         {
3547                 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3548                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3549                         && strinfo->src_port==port_src_fwd
3550                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3551                         && strinfo->dest_port==port_dst_fwd)
3552                 {
3553                         filtered_list = g_list_prepend(filtered_list, strinfo);
3554                 }
3555
3556                 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3557                         && strinfo->src_port==port_src_rev
3558                         && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3559                         && strinfo->dest_port==port_dst_rev)
3560                 {
3561                         ++nfound;
3562                         filtered_list = g_list_append(filtered_list, strinfo);
3563                         if (ssrc_rev==0)
3564                                 ssrc_rev = strinfo->ssrc;
3565                 }
3566
3567                 strinfo_list = g_list_next(strinfo_list);
3568         }
3569
3570         /* if more than one reverse streams found, we let the user choose the right one */
3571         if (nfound>1) {
3572                 rtpstream_dlg_show(filtered_list);
3573                 return;
3574         }
3575         else {
3576                 rtp_analysis(
3577                         &ip_src_fwd,
3578                         port_src_fwd,
3579                         &ip_dst_fwd,
3580                         port_dst_fwd,
3581                         ssrc_fwd,
3582                         &ip_src_rev,
3583                         port_src_rev,
3584                         &ip_dst_rev,
3585                         port_dst_rev,
3586                         ssrc_rev
3587                         );
3588         }
3589 }
3590
3591 /****************************************************************************/
3592 static void
3593 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3594 {
3595         rtp_analysis_cb(NULL, NULL);
3596 }
3597
3598 /****************************************************************************/
3599 void
3600 register_tap_listener_rtp_analysis(void)
3601 {
3602         register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3603
3604         register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3605             rtp_analysis_cb, NULL, NULL, NULL);
3606 }