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