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