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