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