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