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