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