Remove unneeded includes from ui folder
[metze/wireshark/wip.git] / ui / gtk / rtp_analysis.c
1 /* rtp_analysis.c
2  * RTP analysis addition for Wireshark
3  *
4  * Copyright 2003, Alcatel Business Systems
5  * By Lars Ruoff <lars.ruoff@gmx.net>
6  *
7  * based on tap_rtp.c
8  * Copyright 2003, Iskratel, Ltd, Kranj
9  * By Miha Jemec <m.jemec@iskratel.si>
10  *
11  * Graph. Copyright 2004, Verso Technology
12  * By Alejandro Vaquero <alejandro.vaquero@verso.com>
13  * Based on io_stat.c by Ronnie Sahlberg
14  *
15  * Wireshark - Network traffic analyzer
16  * By Gerald Combs <gerald@wireshark.org>
17  * Copyright 1998 Gerald Combs
18  *
19  * This program is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU General Public License
21  * as published by the Free Software Foundation; either version 2
22  * of the License, or (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation,  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32  */
33
34 #include "config.h"
35
36 #include <stdlib.h>
37 #include <math.h>
38 #include <string.h>
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47
48 #include <gtk/gtk.h>
49
50 #include <wsutil/file_util.h>
51 #include <wsutil/tempfile.h>
52 #include <wsutil/g711.h>
53 #include <wsutil/pint.h>
54
55 #include <epan/epan_dissect.h>
56 #include <wsutil/filesystem.h>
57 #include <epan/tap.h>
58 #include <epan/dissectors/packet-rtp.h>
59 #include <epan/rtp_pt.h>
60 #include <epan/addr_resolv.h>
61 #include <epan/stat_tap_ui.h>
62 #include <epan/strutil.h>
63
64 #include <epan/stat_groups.h>
65
66 #include "ui/util.h"
67 #include "ui/alert_box.h"
68 #include "ui/last_open_dir.h"
69 #include "ui/progress_dlg.h"
70 #include "ui/simple_dialog.h"
71 #include "ui/utf8_entities.h"
72
73
74 #include "ui/gtk/gtkglobals.h"
75 #include "ui/gtk/dlg_utils.h"
76 #include "ui/gtk/file_dlg.h"
77 #include "ui/gtk/gui_utils.h"
78 #include "ui/gtk/gui_stat_menu.h"
79 #include "ui/gtk/pixmap_save.h"
80 #include "ui/gtk/main.h"
81 #include "ui/rtp_analysis.h"
82 #include "ui/rtp_stream.h"
83 #include "ui/gtk/rtp_stream_dlg.h"
84 #include "ui/gtk/stock_icons.h"
85
86 #ifdef HAVE_LIBPORTAUDIO
87 #include "ui/voip_calls.h"
88 #include "ui/gtk/graph_analysis.h"
89 #include "ui/gtk/rtp_player.h"
90 #endif /* HAVE_LIBPORTAUDIO */
91
92 #include "ui/gtk/old-gtk-compat.h"
93
94 #include "frame_tvbuff.h"
95
96 void register_tap_listener_rtp_analysis(void);
97
98 enum
99 {
100         PACKET_COLUMN,
101         SEQUENCE_COLUMN,
102         TIMESTAMP_COLUMN,
103         DELTA_COLUMN,
104         JITTER_COLUMN,
105         SKEW_COLUMN,
106         IPBW_COLUMN,
107         MARKER_COLUMN,
108         STATUS_COLUMN,
109         DATE_COLUMN,
110         LENGTH_COLUMN,
111         FOREGROUND_COLOR_COL,
112         BACKGROUND_COLOR_COL,
113         N_COLUMN /* The number of columns */
114 };
115 /****************************************************************************/
116
117 #define NUM_COLS 9
118 #define NUM_GRAPH_ITEMS 100000
119 #define MAX_YSCALE 16
120 #define DEFAULT_YSCALE_INDEX 0 /* AUTO_MAX_YSCALE */
121 #define AUTO_MAX_YSCALE 0
122 #define MAX_GRAPHS 6
123 #define GRAPH_FWD_JITTER 0
124 #define GRAPH_FWD_DIFF 1
125 #define GRAPH_FWD_DELTA 2
126 #define GRAPH_REV_JITTER 3
127 #define GRAPH_REV_DIFF 4
128 #define GRAPH_REV_DELTA 5
129
130 static guint32 yscale_max[MAX_YSCALE] = {
131         AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000,
132         1000000, 2000000, 5000000, 10000000, 20000000, 50000000
133 };
134
135 #define MAX_PIXELS_PER_TICK 4
136 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
137 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
138
139 static const char *graph_descr[MAX_GRAPHS] = {
140         "Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"
141 };
142
143 /* unit is in ms */
144 #define MAX_TICK_VALUES 5
145 #define DEFAULT_TICK_VALUE_INDEX 1
146 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
147
148 typedef struct _dialog_graph_graph_item_t {
149         guint32 value;
150         guint32 flags;
151 } dialog_graph_graph_item_t;
152
153 typedef struct _dialog_graph_graph_t {
154         struct _user_data_t *ud;
155         dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
156         int        plot_style;
157         gboolean   display;
158         GtkWidget *display_button;
159         int        hf_index;
160         GdkRGBA    rgba_color;
161         GdkColor   color;
162         gchar      title[100];
163 } dialog_graph_graph_t;
164
165
166 typedef struct _dialog_graph_t {
167         gboolean         needs_redraw;
168         gint32           interval;     /* measurement interval in ms */
169         guint32          last_interval;
170         guint32          max_interval; /* XXX max_interval and num_items are redundant */
171         guint32          num_items;
172         double           start_time;
173
174         struct _dialog_graph_graph_t graph[MAX_GRAPHS];
175         GtkWidget       *window;
176         GtkWidget       *draw_area;
177 #if GTK_CHECK_VERSION(2,22,0)
178         cairo_surface_t *surface;
179 #else
180         GdkPixmap       *pixmap;
181 #endif
182         GtkAdjustment   *scrollbar_adjustment;
183         GtkWidget       *scrollbar;
184         int              surface_width;
185         int              surface_height;
186         int              pixels_per_tick;
187         int              max_y_units;
188 } dialog_graph_t;
189
190 typedef struct _dialog_data_t {
191         GtkWidget        *window;
192         GtkWidget        *list_fwd;
193         GtkTreeIter       iter;
194         GtkWidget        *list_rev;
195         GtkWidget        *label_stats_fwd;
196         GtkWidget        *label_stats_rev;
197         GtkWidget        *selected_list;
198         guint             number_of_nok;
199         GtkTreeSelection *selected_list_sel;
200         gint              selected_list_row;
201         GtkWidget        *notebook;
202         GtkWidget        *save_voice_as_w;
203         GtkWidget        *save_csv_as_w;
204         gulong            notebook_signal_id;
205         dialog_graph_t    dialog_graph;
206 } dialog_data_t;
207
208 #define OK_TEXT "[ Ok ]"
209
210 /* type of error when saving voice in a file didn't succeed */
211 typedef enum {
212         TAP_RTP_WRONG_CODEC,
213         TAP_RTP_WRONG_LENGTH,
214         TAP_RTP_PADDING_ERROR,
215         TAP_RTP_SHORT_FRAME,
216         TAP_RTP_FILE_OPEN_ERROR,
217         TAP_RTP_FILE_WRITE_ERROR,
218         TAP_RTP_NO_DATA
219 } error_type_t;
220
221 typedef struct _tap_rtp_save_info_t {
222         FILE *fp;
223         guint32 count;
224         error_type_t error_type;
225         gboolean saved;
226 } tap_rtp_save_info_t;
227
228
229 /* structure that holds the information about the forward and reversed direction */
230 struct _info_direction {
231         tap_rtp_stat_t statinfo;
232         tap_rtp_save_info_t saveinfo;
233 };
234
235 #define SILENCE_PCMU    (guint8)0xFF
236 #define SILENCE_PCMA    (guint8)0x55
237
238 /* structure that holds general information about the connection
239 * and structures for both directions */
240 typedef struct _user_data_t {
241         /* tap associated data*/
242         address src_fwd;
243         guint32 port_src_fwd;
244         address dst_fwd;
245         guint32 port_dst_fwd;
246         guint32 ssrc_fwd;
247         address src_rev;
248         guint32 port_src_rev;
249         address dst_rev;
250         guint32 port_dst_rev;
251         guint32 ssrc_rev;
252
253         struct _info_direction forward;
254         struct _info_direction reversed;
255
256         char *f_tempname;
257         char *r_tempname;
258
259         /* dialog associated data */
260         dialog_data_t dlg;
261
262 } user_data_t;
263
264
265 /* Column titles. */
266 static const gchar *titles[11] =  {
267         "Packet",
268         "Sequence",
269         "Time stamp",
270         "Delta (ms)",
271         "Jitter (ms)",
272         "Skew(ms)",
273         "IP BW (kbps)",
274         "Marker",
275         "Status",
276         "Date",
277         "Length"
278 };
279
280 #define SAVE_FORWARD_DIRECTION_MASK 0x01
281 #define SAVE_REVERSE_DIRECTION_MASK 0x02
282 #define SAVE_BOTH_DIRECTION_MASK    (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
283
284 #define SAVE_NONE_FORMAT 0
285 #define SAVE_WAV_FORMAT  1
286 #define SAVE_AU_FORMAT   2
287 #define SAVE_SW_FORMAT   3
288 #define SAVE_RAW_FORMAT  4
289
290
291 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
292
293 /****************************************************************************/
294 static void
295 enable_graph(dialog_graph_graph_t *dgg)
296 {
297
298         dgg->display = TRUE;
299
300 }
301
302 static void dialog_graph_reset(user_data_t* user_data);
303
304
305
306 /****************************************************************************/
307 /* TAP FUNCTIONS */
308
309 /****************************************************************************/
310 /* when there is a [re]reading of packet's */
311 static void
312 rtp_reset(void *user_data_arg)
313 {
314         user_data_t *user_data = (user_data_t *)user_data_arg;
315
316         user_data->forward.statinfo.first_packet    = TRUE;
317         user_data->reversed.statinfo.first_packet   = TRUE;
318         user_data->forward.statinfo.max_delta       = 0;
319         user_data->reversed.statinfo.max_delta      = 0;
320         user_data->forward.statinfo.max_jitter      = 0;
321         user_data->reversed.statinfo.max_jitter     = 0;
322         user_data->forward.statinfo.max_skew        = 0;
323         user_data->reversed.statinfo.max_skew       = 0;
324         user_data->forward.statinfo.mean_jitter     = 0;
325         user_data->reversed.statinfo.mean_jitter    = 0;
326         user_data->forward.statinfo.delta           = 0;
327         user_data->reversed.statinfo.delta          = 0;
328         user_data->forward.statinfo.diff            = 0;
329         user_data->reversed.statinfo.diff           = 0;
330         user_data->forward.statinfo.jitter          = 0;
331         user_data->reversed.statinfo.jitter         = 0;
332         user_data->forward.statinfo.skew            = 0;
333         user_data->reversed.statinfo.skew           = 0;
334         user_data->forward.statinfo.sumt            = 0;
335         user_data->reversed.statinfo.sumt           = 0;
336         user_data->forward.statinfo.sumTS           = 0;
337         user_data->reversed.statinfo.sumTS          = 0;
338         user_data->forward.statinfo.sumt2           = 0;
339         user_data->reversed.statinfo.sumt2          = 0;
340         user_data->forward.statinfo.sumtTS          = 0;
341         user_data->reversed.statinfo.sumtTS         = 0;
342         user_data->forward.statinfo.bandwidth       = 0;
343         user_data->reversed.statinfo.bandwidth      = 0;
344         user_data->forward.statinfo.total_bytes     = 0;
345         user_data->reversed.statinfo.total_bytes    = 0;
346         user_data->forward.statinfo.bw_start_index  = 0;
347         user_data->reversed.statinfo.bw_start_index = 0;
348         user_data->forward.statinfo.bw_index        = 0;
349         user_data->reversed.statinfo.bw_index       = 0;
350         user_data->forward.statinfo.timestamp       = 0;
351         user_data->reversed.statinfo.timestamp      = 0;
352         user_data->forward.statinfo.max_nr          = 0;
353         user_data->reversed.statinfo.max_nr         = 0;
354         user_data->forward.statinfo.total_nr        = 0;
355         user_data->reversed.statinfo.total_nr       = 0;
356         user_data->forward.statinfo.sequence        = 0;
357         user_data->reversed.statinfo.sequence       = 0;
358         user_data->forward.statinfo.start_seq_nr    = 0;
359         user_data->reversed.statinfo.start_seq_nr   = 1; /* 1 is ok (for statistics in reversed direction) */
360         user_data->forward.statinfo.stop_seq_nr     = 0;
361         user_data->reversed.statinfo.stop_seq_nr    = 0;
362         user_data->forward.statinfo.cycles          = 0;
363         user_data->reversed.statinfo.cycles         = 0;
364         user_data->forward.statinfo.under           = FALSE;
365         user_data->reversed.statinfo.under          = FALSE;
366         user_data->forward.statinfo.start_time      = 0;
367         user_data->reversed.statinfo.start_time     = 0;
368         user_data->forward.statinfo.time            = 0;
369         user_data->reversed.statinfo.time           = 0;
370         user_data->forward.statinfo.reg_pt          = PT_UNDEFINED;
371         user_data->reversed.statinfo.reg_pt         = PT_UNDEFINED;
372
373         user_data->forward.saveinfo.count           = 0;
374         user_data->reversed.saveinfo.count          = 0;
375         user_data->forward.saveinfo.saved           = FALSE;
376         user_data->reversed.saveinfo.saved          = FALSE;
377
378         /* clear the dialog box lists */
379         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
380         gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
381
382         /* reset graph info */
383         dialog_graph_reset(user_data);
384
385 #ifdef HAVE_LIBPORTAUDIO
386         /* reset the RTP player */
387         reset_rtp_player();
388 #endif
389         /* XXX check for error at fclose? */
390         if (user_data->forward.saveinfo.fp != NULL)
391                 fclose(user_data->forward.saveinfo.fp);
392         if (user_data->reversed.saveinfo.fp != NULL)
393                 fclose(user_data->reversed.saveinfo.fp);
394         user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
395         if (user_data->forward.saveinfo.fp == NULL)
396                 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
397         user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
398         if (user_data->reversed.saveinfo.fp == NULL)
399                 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
400         return;
401 }
402
403 /****************************************************************************/
404 static gboolean
405 rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
406 {
407         dialog_graph_graph_item_t *it;
408         guint32 idx;
409         double  rtp_time;
410
411         /*
412         * We sometimes get called when dgg is disabled.
413         * This is a bug since the tap listener should be removed first
414         */
415         if (!dgg->display) {
416                 return FALSE;
417         }
418
419         dgg->ud->dlg.dialog_graph.needs_redraw = TRUE;
420
421         /*
422         * Find which interval this is supposed to go in and store the
423         * interval index as idx
424         */
425         if (dgg->ud->dlg.dialog_graph.start_time == -1) { /* it is the first */
426                 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
427         }
428         rtp_time = nstime_to_msec(&pinfo->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
429         if (rtp_time < 0) {
430                 return FALSE;
431         }
432         idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
433
434         /* some sanity checks */
435         if (idx >= NUM_GRAPH_ITEMS) {
436                 return FALSE;
437         }
438
439         /* update num_items */
440         if (idx > dgg->ud->dlg.dialog_graph.num_items) {
441                 dgg->ud->dlg.dialog_graph.num_items = idx;
442                 dgg->ud->dlg.dialog_graph.max_interval = idx*dgg->ud->dlg.dialog_graph.interval;
443         }
444
445         /*
446         * Find the appropriate dialog_graph_graph_item_t structure
447         */
448         it = &dgg->items[idx];
449
450         /*
451         * Use the max value to highlight RTP problems
452         */
453         if (value > it->value) {
454                 it->value = value;
455         }
456         it->flags = it->flags | statinfo->flags;
457
458         return TRUE;
459 }
460
461 /****************************************************************************/
462 /* here we can redraw the output */
463 /* not used yet */
464 static void
465 rtp_draw(void *prs _U_)
466 {
467         return;
468 }
469
470 /* forward declarations */
471 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
472                         double delta, double jitter, double skew , double bandwidth, gchar *status, gboolean marker,
473                         gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags);
474
475 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
476         tap_rtp_stat_t *statinfo, packet_info *pinfo,
477         const struct _rtp_info *rtpinfo);
478
479 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
480                                    tap_rtp_stat_t *statinfo,
481                                    packet_info *pinfo,
482                                    const struct _rtp_info *rtpinfo);
483
484
485 /****************************************************************************/
486 /* whenever a RTP packet is seen by the tap listener */
487 static int
488 rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
489 {
490         user_data_t            *user_data    = (user_data_t *)user_data_arg;
491         const struct _rtp_info *rtpinfo      = (const struct _rtp_info *)rtpinfo_arg;
492         gboolean                rtp_selected = FALSE;
493
494         /* we ignore packets that are not displayed */
495         if (pinfo->fd->flags.passed_dfilter == 0)
496                 return 0;
497         /* also ignore RTP Version != 2 */
498         else if (rtpinfo->info_version != 2)
499                 return 0;
500         /* is it the forward direction?  */
501         else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
502                  && (CMP_ADDRESS(&(user_data->src_fwd), &(pinfo->src)) == 0)
503                  && (user_data->port_src_fwd == pinfo->srcport)
504                  && (CMP_ADDRESS(&(user_data->dst_fwd), &(pinfo->dst)) == 0)
505                  && (user_data->port_dst_fwd == pinfo->destport))  {
506                 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
507                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
508                         &(user_data->forward.statinfo), pinfo,
509                         (guint32)(user_data->forward.statinfo.jitter*1000));
510                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
511                         &(user_data->forward.statinfo), pinfo,
512                         (guint32)(user_data->forward.statinfo.diff*1000));
513                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
514                         &(user_data->forward.statinfo), pinfo,
515                         (guint32)(user_data->forward.statinfo.delta*1000));
516                 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
517                         &(user_data->forward.statinfo), pinfo, rtpinfo);
518                 rtp_packet_save_payload(&(user_data->forward.saveinfo),
519                         &(user_data->forward.statinfo), pinfo, rtpinfo);
520                 rtp_selected = TRUE;
521         }
522         /* is it the reversed direction? */
523         else if (user_data->ssrc_rev == rtpinfo->info_sync_src
524                  && (CMP_ADDRESS(&(user_data->src_rev), &(pinfo->src)) == 0)
525                  && (user_data->port_src_rev == pinfo->srcport)
526                  && (CMP_ADDRESS(&(user_data->dst_rev), &(pinfo->dst)) == 0)
527                  && (user_data->port_dst_rev == pinfo->destport))  {
528                 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
529                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
530                         &(user_data->reversed.statinfo), pinfo,
531                         (guint32)(user_data->reversed.statinfo.jitter*1000));
532                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
533                         &(user_data->reversed.statinfo), pinfo,
534                         (guint32)(user_data->reversed.statinfo.diff*1000));
535                 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
536                         &(user_data->reversed.statinfo), pinfo,
537                         (guint32)(user_data->reversed.statinfo.delta*1000));
538                 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
539                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
540                 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
541                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
542                 rtp_selected = TRUE;
543         }
544         /* add this RTP for future listening using the RTP Player*/
545         if (rtp_selected) {
546 #ifdef HAVE_LIBPORTAUDIO
547                 add_rtp_packet(rtpinfo, pinfo);
548 #endif
549         }
550
551         return 0;
552 }
553
554 /*
555 Replaced by using the strings instead.
556 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
557 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
558 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
559 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
560 GdkColor yellow = {0, 0xffff, 0xffff, 0x0000};
561 COLOR_T_EVENT g_snprintf(color_str, sizeof(color_str), "#ef8c bfff ffff");
562 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
563 */
564 /****************************************************************************/
565 /* adds statistics information from the packet to the list */
566 static int
567 rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
568                     tap_rtp_stat_t *statinfo, packet_info *pinfo,
569                     const struct _rtp_info *rtpinfo)
570 {
571         guint16    msecs;
572         gchar      timeStr[32];
573         struct tm *tm_tmp;
574         time_t     then;
575         gchar      status[80];
576         gchar      color_str[14];
577
578         then = pinfo->fd->abs_ts.secs;
579         msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
580         tm_tmp = localtime(&then);
581         g_snprintf(timeStr, sizeof(timeStr), "%02d/%02d/%04d %02d:%02d:%02d.%03d",
582                 tm_tmp->tm_mon + 1,
583                 tm_tmp->tm_mday,
584                 tm_tmp->tm_year + 1900,
585                 tm_tmp->tm_hour,
586                 tm_tmp->tm_min,
587                 tm_tmp->tm_sec,
588                 msecs);
589
590         /* Default to using black on white text if nothing below overrides it */
591         g_snprintf(color_str, sizeof(color_str), "#ffffffffffff");
592
593         if (statinfo->pt == PT_CN) {
594                 g_snprintf(status, sizeof(status), "Comfort noise (PT=13, RFC 3389)");
595                 /* color = COLOR_CN; */
596                 g_snprintf(color_str, sizeof(color_str), "#bfffbfffffff");
597         }
598         else if (statinfo->pt == PT_CN_OLD) {
599                 g_snprintf(status, sizeof(status), "Comfort noise (PT=19, reserved)");
600                 /* color = COLOR_CN; */
601                 g_snprintf(color_str, sizeof(color_str), "#bfffbfffffff");
602         }
603         else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
604                 g_snprintf(status, sizeof(status), "Wrong sequence nr.");
605                 /* color = COLOR_ERROR; */
606                 g_snprintf(color_str, sizeof(color_str), "#ffffbfffbfff");
607         }
608         else if (statinfo->flags & STAT_FLAG_DUP_PKT) {
609                 g_snprintf(status, sizeof(status), "Suspected duplicate(MAC address) only delta time calculated");
610                 /* color = Yellow; */
611                 g_snprintf(color_str, sizeof(color_str), "#ffffffff0000");
612         }
613         else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
614                 if (statinfo->flags & STAT_FLAG_PT_T_EVENT) {
615                         g_snprintf(status, sizeof(status), "Payload changed to PT=%u telephone/event", statinfo->pt);
616                 }else{
617                         g_snprintf(status, sizeof(status), "Payload changed to PT=%u", statinfo->pt);
618                 }
619                 /* color = COLOR_WARNING; */
620                 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
621         }
622         else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
623                 g_snprintf(status, sizeof(status), "Incorrect timestamp");
624                 /* color = COLOR_WARNING; */
625                 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
626         }
627         else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
628                 &&  !(statinfo->flags & STAT_FLAG_FIRST)
629                 &&  !(statinfo->flags & STAT_FLAG_PT_CN)
630                 &&  (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
631                 &&  !(statinfo->flags & STAT_FLAG_MARKER)) {
632                 g_snprintf(status, sizeof(status), "Marker missing?");
633                 /* color = COLOR_WARNING; */
634                 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
635         }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT) {
636                 g_snprintf(status, sizeof(status), "PT=%u telephone/event", statinfo->pt);
637                 /* XXX add color? */
638                 /* color = COLOR_T_EVENT; */
639                 g_snprintf(color_str, sizeof(color_str), "#ef8cbfffffff");
640         }else {
641                 if (statinfo->flags & STAT_FLAG_MARKER) {
642                         /* color = COLOR_WARNING; */
643                         g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
644                 }
645                 g_snprintf(status, sizeof(status), OK_TEXT);
646         }
647         /*  is this the first packet we got in this direction? */
648         if (statinfo->flags & STAT_FLAG_FIRST) {
649                 add_to_list(list, user_data,
650                         pinfo->fd->num, rtpinfo->info_seq_num,
651                         statinfo->timestamp,
652                         0,
653                         0,
654                         0,
655                         statinfo->bandwidth,
656                         status,
657                         rtpinfo->info_marker_set,
658                         timeStr, pinfo->fd->pkt_len,
659                         color_str,
660                         statinfo->flags);
661         }
662         else {
663                 add_to_list(list, user_data,
664                         pinfo->fd->num, rtpinfo->info_seq_num,
665                         statinfo->timestamp,
666                         statinfo->delta,
667                         statinfo->jitter,
668                         statinfo->skew,
669                         statinfo->bandwidth,
670                         status,
671                         rtpinfo->info_marker_set,
672                         timeStr, pinfo->fd->pkt_len,
673                         color_str,
674                         statinfo->flags);
675         }
676         return 0;
677 }
678
679 #define MAX_SILENCE_TICKS 1000000
680 /****************************************************************************/
681 static int
682 rtp_packet_save_payload(tap_rtp_save_info_t    *saveinfo,
683                         tap_rtp_stat_t         *statinfo,
684                         packet_info            *pinfo,
685                         const struct _rtp_info *rtpinfo)
686 {
687         guint         i;
688         const guint8 *data;
689         guint8        tmp;
690         size_t        nchars;
691
692         /*  is this the first packet we got in this direction? */
693         if (statinfo->flags & STAT_FLAG_FIRST) {
694                 if (saveinfo->fp == NULL) {
695                         saveinfo->saved = FALSE;
696                         saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
697                 }
698                 else
699                         saveinfo->saved = TRUE;
700         }
701
702         /* save the voice information */
703         /* if there was already an error, we quit */
704         if (saveinfo->saved == FALSE)
705                 return 0;
706
707         /* if the captured length and packet length aren't equal, we quit
708         * if also the RTP dissector thinks there is some information missing */
709         if ((pinfo->fd->pkt_len != pinfo->fd->cap_len)
710             && (!rtpinfo->info_all_data_present)) {
711                 saveinfo->saved = FALSE;
712                 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
713                 return 0;
714         }
715
716         /* if padding bit is set, but the padding count is bigger
717         * then the whole RTP data - error with padding count */
718         if ( (rtpinfo->info_padding_set != FALSE)
719              && (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
720                 saveinfo->saved      = FALSE;
721                 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
722                 return 0;
723         }
724
725         /* do we need to insert some silence? */
726         if ((rtpinfo->info_marker_set)
727             && ! (statinfo->flags & STAT_FLAG_FIRST)
728             && ! (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
729             && (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) )  {
730                 /* the amount of silence should be the difference between
731                 * the last timestamp and the current one minus x
732                 * x should equal the amount of information in the last frame
733                 * XXX not done yet */
734                 for (i = 0;
735                      (i < (statinfo->delta_timestamp - rtpinfo->info_payload_len - rtpinfo->info_padding_count))
736                              && (i < MAX_SILENCE_TICKS);
737                      i++) {
738                         switch (statinfo->reg_pt) {
739                         case PT_PCMU:
740                                 tmp = SILENCE_PCMU;
741                                 break;
742                         case PT_PCMA:
743                                 tmp = SILENCE_PCMA;
744                                 break;
745                         default:
746                                 tmp = 0;
747                                 break;
748                         }
749                         nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
750                         if (nchars != 1) {
751                                 /* Write error or short write */
752                                 saveinfo->saved = FALSE;
753                                 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
754                                 return 0;
755                         }
756                         saveinfo->count++;
757                 }
758                 fflush(saveinfo->fp);
759         }
760
761
762         if ((rtpinfo->info_payload_type == PT_CN)
763             || (rtpinfo->info_payload_type == PT_CN_OLD)) {
764         }
765         /* all other payloads */
766         else {
767                 if (!rtpinfo->info_all_data_present) {
768                         /* Not all the data was captured. */
769                         saveinfo->saved = FALSE;
770                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
771                         return 0;
772                 }
773
774                 /* we put the pointer at the beginning of the RTP
775                 * payload, that is, at the beginning of the RTP data
776                 * plus the offset of the payload from the beginning
777                 * of the RTP data */
778                 data   = rtpinfo->info_data + rtpinfo->info_payload_offset;
779                 nchars = fwrite(data, sizeof(unsigned char),
780                                 (rtpinfo->info_payload_len - rtpinfo->info_padding_count),
781                                 saveinfo->fp);
782                 if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
783                         /* Write error or short write */
784                         saveinfo->saved = FALSE;
785                         saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
786                         return 0;
787                 }
788                 saveinfo->count += (rtpinfo->info_payload_len - rtpinfo->info_padding_count);
789
790                 fflush(saveinfo->fp);
791                 saveinfo->saved = TRUE;
792                 return 0;
793         }
794
795         return 0;
796 }
797
798
799 /****************************************************************************/
800 /* CALLBACKS */
801
802 /****************************************************************************/
803
804 /****************************************************************************/
805 /* close the dialog window and remove the tap listener */
806 static void
807 on_destroy(GtkWidget *win _U_, user_data_t *user_data)
808 {
809         /* remove tap listener */
810         remove_tap_listener(user_data);
811
812         /* close and remove temporary files */
813         if (user_data->forward.saveinfo.fp != NULL)
814                 fclose(user_data->forward.saveinfo.fp);
815         if (user_data->reversed.saveinfo.fp != NULL)
816                 fclose(user_data->reversed.saveinfo.fp);
817         /*XXX: test for error **/
818         ws_remove(user_data->f_tempname);
819         ws_remove(user_data->r_tempname);
820
821 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
822         /* destroy save_voice_as window if open */
823         if (user_data->dlg.save_voice_as_w != NULL)
824                 window_destroy(user_data->dlg.save_voice_as_w);
825 #endif
826         /* destroy graph window if open */
827         if (user_data->dlg.dialog_graph.window != NULL)
828                 window_destroy(user_data->dlg.dialog_graph.window);
829
830         /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows
831          *  is destroyed and cause an exception using GTK1*/
832         g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
833
834         g_free(user_data->f_tempname);
835         g_free(user_data->r_tempname);
836         g_free(user_data);
837 }
838
839
840 /****************************************************************************/
841 static void
842 on_notebook_switch_page(GtkNotebook *notebook  _U_,
843                         gpointer    *page      _U_,
844                         gint         page_num,
845                         user_data_t *user_data)
846 {
847         user_data->dlg.selected_list =
848                 (page_num == 0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
849
850         user_data->dlg.selected_list_row = 0;
851 }
852
853 /****************************************************************************/
854
855 static void
856 on_list_select_row(GtkTreeSelection *selection,
857                    user_data_t *user_data /*gpointer data */)
858 {
859         user_data->dlg.selected_list_sel = selection;
860 }
861
862
863 /****************************************************************************/
864 static void
865 dialog_graph_set_title(user_data_t* user_data)
866 {
867         char            *title;
868
869         if (!user_data->dlg.dialog_graph.window) {
870                 return;
871         }
872
873         title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u   Reverse: %s:%u to %s:%u",
874                         ep_address_to_display(&(user_data->src_fwd)),
875                         user_data->port_src_fwd,
876                         ep_address_to_display(&(user_data->dst_fwd)),
877                         user_data->port_dst_fwd,
878                         ep_address_to_display(&(user_data->src_rev)),
879                         user_data->port_src_rev,
880                         ep_address_to_display(&(user_data->dst_rev)),
881                         user_data->port_dst_rev);
882
883         gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
884         g_free(title);
885
886 }
887
888
889 /****************************************************************************/
890 static void
891 dialog_graph_reset(user_data_t* user_data)
892 {
893         int i, j;
894
895         user_data->dlg.dialog_graph.needs_redraw = TRUE;
896         for (i = 0; i < MAX_GRAPHS; i++) {
897                 for (j = 0; j < NUM_GRAPH_ITEMS; j++) {
898                         dialog_graph_graph_item_t *dggi;
899                         dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
900                         dggi->value = 0;
901                         dggi->flags = 0;
902                 }
903         }
904         user_data->dlg.dialog_graph.last_interval = 0xffffffff;
905         user_data->dlg.dialog_graph.max_interval = 0;
906         user_data->dlg.dialog_graph.num_items = 0;
907
908         /* create the color titles near the filter buttons */
909         for (i = 0; i < MAX_GRAPHS; i++) {
910                 /* it is forward */
911                 if (i < (MAX_GRAPHS/2)) {
912                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
913                                    sizeof(user_data->dlg.dialog_graph.graph[0].title),
914                                    "%s: %s:%u to %s:%u (SSRC=0x%X)",
915                                    graph_descr[i],
916                                    ep_address_to_display(&(user_data->src_fwd)),
917                                    user_data->port_src_fwd,
918                                    ep_address_to_display(&(user_data->dst_fwd)),
919                                    user_data->port_dst_fwd,
920                                    user_data->ssrc_fwd);
921                 /* it is reverse */
922                 } else {
923                         g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
924                                    sizeof(user_data->dlg.dialog_graph.graph[0].title),
925                                    "%s: %s:%u to %s:%u (SSRC=0x%X)",
926                                    graph_descr[i],
927                                    ep_address_to_display(&(user_data->src_rev)),
928                                    user_data->port_src_rev,
929                                    ep_address_to_display(&(user_data->dst_rev)),
930                                    user_data->port_dst_rev,
931                                    user_data->ssrc_rev);
932                 }
933         }
934
935         dialog_graph_set_title(user_data);
936 }
937
938 /****************************************************************************/
939 static guint32
940 get_it_value(dialog_graph_graph_t *dgg, int idx)
941 {
942         dialog_graph_graph_item_t *it;
943
944         it = &dgg->items[idx];
945
946         return it->value;
947 }
948
949 /****************************************************************************/
950 static void
951 print_time_scale_string(char *buf, int buf_len, guint32 t)
952 {
953         if (t >= 10000000) {
954                 g_snprintf(buf, buf_len, "%ds", t/1000000);
955         } else if (t >= 1000000) {
956                 g_snprintf(buf, buf_len, "%d.%03ds", t/1000000, (t%1000000)/1000);
957         } else if (t >= 10000) {
958                 g_snprintf(buf, buf_len, "%dms", t/1000);
959         } else if (t >= 1000) {
960                 g_snprintf(buf, buf_len, "%d.%03dms", t/1000, t%1000);
961         } else {
962                 g_snprintf(buf, buf_len, "%dus", t);
963         }
964 }
965
966 /****************************************************************************/
967 static void
968 dialog_graph_draw(user_data_t* user_data)
969 {
970         int            i, lwidth;
971         guint32        last_interval, first_interval, interval_delta, delta_multiplier;
972         gint32         current_interval;
973         guint32        left_x_border;
974         guint32        right_x_border;
975         guint32        top_y_border;
976         guint32        bottom_y_border;
977         PangoLayout   *layout;
978         int            label_width, label_height;
979         int            label_width_mid, label_height_mid;
980         guint32        draw_width, draw_height;
981         char           label_string[15];
982         GtkAllocation  widget_alloc;
983         cairo_t       *cr;
984
985         /* new variables */
986         guint32 num_time_intervals;
987         guint32 max_value;              /* max value of seen data */
988         guint32 max_y;                  /* max value of the Y scale */
989
990         if (!user_data->dlg.dialog_graph.needs_redraw) {
991                 return;
992         }
993         user_data->dlg.dialog_graph.needs_redraw = FALSE;
994
995         /*
996          * Find the length of the intervals we have data for
997          * so we know how large arrays we need to malloc()
998          */
999         num_time_intervals = user_data->dlg.dialog_graph.num_items;
1000         /* if there isn't anything to do, just return */
1001         if (num_time_intervals == 0) {
1002                 return;
1003         }
1004         num_time_intervals+=1;
1005         /* XXX move this check to _packet() */
1006         if (num_time_intervals>NUM_GRAPH_ITEMS) {
1007                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1008                 return;
1009         }
1010
1011         /*
1012          * find the max value so we can autoscale the y axis
1013          */
1014         max_value = 0;
1015         for (i = 0; i < MAX_GRAPHS; i++) {
1016                 int idx;
1017
1018                 if (!user_data->dlg.dialog_graph.graph[i].display) {
1019                         continue;
1020                 }
1021                 for (idx = 0; (guint32)(idx) < num_time_intervals; idx++) {
1022                         guint32 val;
1023
1024                         val = get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1025
1026                         /* keep track of the max value we have encountered */
1027                         if (val>max_value) {
1028                                 max_value = val;
1029                         }
1030                 }
1031         }
1032
1033         /*
1034          * Clear out old plot
1035          */
1036 #if GTK_CHECK_VERSION(2,22,0)
1037         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1038 #else
1039         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1040 #endif
1041         cairo_set_source_rgb (cr, 1, 1, 1);
1042         gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
1043         cairo_rectangle (cr,
1044                 0,
1045                 0,
1046                 widget_alloc.width,
1047                 widget_alloc.height);
1048         cairo_fill (cr);
1049         cairo_destroy (cr);
1050
1051         /*
1052          * Calculate the y scale we should use
1053          */
1054         if (user_data->dlg.dialog_graph.max_y_units == AUTO_MAX_YSCALE) {
1055                 max_y = yscale_max[MAX_YSCALE-1];
1056                 for (i = MAX_YSCALE-1; i > 0; i--) {
1057                         if (max_value<yscale_max[i]) {
1058                                 max_y = yscale_max[i];
1059                         }
1060                 }
1061         } else {
1062                 /* the user had specified an explicit y scale to use */
1063                 max_y = user_data->dlg.dialog_graph.max_y_units;
1064         }
1065
1066         /*
1067          * Calculate size of borders surrounding the plot
1068          * The border on the right side needs to be adjusted depending
1069          * on the width of the text labels.
1070          */
1071         print_time_scale_string(label_string, sizeof(label_string), max_y);
1072         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1073         pango_layout_get_pixel_size(layout, &label_width, &label_height);
1074         print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1075         layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1076         pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1077         if (label_width_mid > label_width) {
1078                 label_width = label_width_mid;
1079                 label_height = label_height_mid;
1080         }
1081
1082         left_x_border   = 10;
1083         right_x_border  = label_width + 20;
1084         top_y_border    = 10;
1085         bottom_y_border = label_height + 20;
1086
1087
1088         /*
1089          * Calculate the size of the drawing area for the actual plot
1090          */
1091         draw_width  = user_data->dlg.dialog_graph.surface_width - right_x_border - left_x_border;
1092         draw_height = user_data->dlg.dialog_graph.surface_height - top_y_border - bottom_y_border;
1093
1094
1095         /*
1096          * Draw the y axis and labels
1097          * (we always draw the y scale with 11 ticks along the axis)
1098          */
1099 #if GTK_CHECK_VERSION(2,22,0)
1100         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1101 #else
1102         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1103 #endif
1104         cairo_set_line_width (cr, 1.0);
1105         cairo_move_to(cr,
1106                       user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1107                       top_y_border + 0.5);
1108         cairo_line_to(cr,
1109                       user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1110                       user_data->dlg.dialog_graph.surface_height - bottom_y_border + 0.5);
1111         cairo_stroke(cr);
1112         cairo_destroy(cr);
1113
1114         for (i = 0; i <= 10; i++) {
1115                 int xwidth;
1116
1117                 xwidth = 5;
1118                 if (!(i%5)) {
1119                         /* first, middle and last tick are slightly longer */
1120                         xwidth = 10;
1121                 }
1122                 /* draw the tick */
1123 #if GTK_CHECK_VERSION(2,22,0)
1124                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1125 #else
1126                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1127 #endif
1128                 cairo_set_line_width (cr, 1.0);
1129                 cairo_move_to(cr,
1130                         user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1131                         user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 + 0.5);
1132
1133                 cairo_line_to(cr,
1134                         user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5 + xwidth,
1135                         user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 + 0.5);
1136                 cairo_stroke(cr);
1137                 cairo_destroy(cr);
1138                 /* draw the labels */
1139                 if (i == 0) {
1140                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1141                         pango_layout_set_text(layout, label_string, -1);
1142                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1143 #if GTK_CHECK_VERSION(2,22,0)
1144                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1145 #else
1146                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1147 #endif
1148                         cairo_move_to (cr,
1149                                 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1150                                 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1151                         pango_cairo_show_layout (cr, layout);
1152                         cairo_destroy (cr);
1153                         cr = NULL;
1154                 }
1155                 if (i == 5) {
1156                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1157                         pango_layout_set_text(layout, label_string, -1);
1158                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1159 #if GTK_CHECK_VERSION(2,22,0)
1160                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1161 #else
1162                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1163 #endif
1164                         cairo_move_to (cr,
1165                                 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1166                                 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1167                         pango_cairo_show_layout (cr, layout);
1168                         cairo_destroy (cr);
1169                         cr = NULL;
1170                 }
1171                 if (i == 10) {
1172                         print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1173                         pango_layout_set_text(layout, label_string, -1);
1174                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1175 #if GTK_CHECK_VERSION(2,22,0)
1176                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1177 #else
1178                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1179 #endif
1180                         cairo_move_to (cr,
1181                                 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1182                                 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1183                         pango_cairo_show_layout (cr, layout);
1184                         cairo_destroy (cr);
1185                         cr = NULL;
1186                 }
1187         }
1188
1189
1190
1191         /*
1192          * if we have not specified the last_interval via the gui,
1193          * then just pick the current end of the capture so that is scrolls
1194          * nicely when doing live captures
1195          */
1196         if (user_data->dlg.dialog_graph.last_interval == 0xffffffff) {
1197                 last_interval = user_data->dlg.dialog_graph.max_interval;
1198         } else {
1199                 last_interval = user_data->dlg.dialog_graph.last_interval;
1200         }
1201
1202
1203
1204
1205 /*XXX*/
1206         /* plot the x-scale */
1207 #if GTK_CHECK_VERSION(2,22,0)
1208         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1209 #else
1210         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1211 #endif
1212         cairo_set_line_width (cr, 1.0);
1213         cairo_move_to(cr,
1214                       left_x_border + 0.5,
1215                       user_data->dlg.dialog_graph.surface_height-bottom_y_border + 1.5);
1216         cairo_line_to(cr,
1217                       user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1218                       user_data->dlg.dialog_graph.surface_height - bottom_y_border + 1.5);
1219         cairo_stroke(cr);
1220         cairo_destroy(cr);
1221
1222         if ((last_interval/user_data->dlg.dialog_graph.interval) > draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1) {
1223                 first_interval = (last_interval/user_data->dlg.dialog_graph.interval)
1224                         - draw_width/user_data->dlg.dialog_graph.pixels_per_tick + 1;
1225                 first_interval *= user_data->dlg.dialog_graph.interval;
1226         } else {
1227                 first_interval = 0;
1228         }
1229
1230         interval_delta   = 1;
1231         delta_multiplier = 5;
1232         while (interval_delta<((last_interval-first_interval)/10)) {
1233                 interval_delta*=delta_multiplier;
1234                 if (delta_multiplier == 5) {
1235                         delta_multiplier = 2;
1236                 } else {
1237                         delta_multiplier = 5;
1238                 }
1239         }
1240
1241         for (current_interval = last_interval;
1242              current_interval > (gint32)first_interval;
1243              current_interval = current_interval-user_data->dlg.dialog_graph.interval) {
1244                 int x, xlen;
1245
1246                 /* if pixels_per_tick is <5, only draw every 10 ticks */
1247                 if ((user_data->dlg.dialog_graph.pixels_per_tick<10)
1248                     && (current_interval%(10*user_data->dlg.dialog_graph.interval))) {
1249                         continue;
1250                 }
1251
1252                 if (current_interval%interval_delta) {
1253                         xlen = 5;
1254                 } else {
1255                         xlen = 17;
1256                 }
1257
1258                 x = draw_width+left_x_border
1259                         - (((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)
1260                            * user_data->dlg.dialog_graph.pixels_per_tick);
1261 #if GTK_CHECK_VERSION(2,22,0)
1262                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1263 #else
1264                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1265 #endif
1266                 cairo_set_line_width (cr, 1.0);
1267                 cairo_move_to(cr,
1268                               x - 1 - user_data->dlg.dialog_graph.pixels_per_tick/2 + 0.5,
1269                               user_data->dlg.dialog_graph.surface_height-bottom_y_border + 1.5);
1270                 cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
1271                 cairo_stroke(cr);
1272                 cairo_destroy(cr);
1273
1274                 if (xlen == 17) {
1275                         if (user_data->dlg.dialog_graph.interval >= 1000) {
1276                                 g_snprintf(label_string, sizeof(label_string),
1277                                            "%ds", current_interval/1000);
1278                         } else if (user_data->dlg.dialog_graph.interval >= 100) {
1279                                 g_snprintf(label_string, sizeof(label_string),
1280                                            "%d.%1ds", current_interval/1000, (current_interval/100)%10);
1281                         } else if (user_data->dlg.dialog_graph.interval >= 10) {
1282                                 g_snprintf(label_string, sizeof(label_string),
1283                                            "%d.%2ds", current_interval/1000, (current_interval/10)%100);
1284                         } else {
1285                                 g_snprintf(label_string, sizeof(label_string),
1286                                            "%d.%3ds", current_interval/1000, current_interval%1000);
1287                         }
1288                         pango_layout_set_text(layout, label_string, -1);
1289                         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1290 #if GTK_CHECK_VERSION(2,22,0)
1291                         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1292 #else
1293                         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1294 #endif
1295                         cairo_move_to (cr,
1296                                 x - 1 - user_data->dlg.dialog_graph.pixels_per_tick/2 - lwidth/2,
1297                                 user_data->dlg.dialog_graph.surface_height-bottom_y_border + 20);
1298                         pango_cairo_show_layout (cr, layout);
1299                         cairo_destroy (cr);
1300                         cr = NULL;
1301                 }
1302
1303         }
1304
1305
1306
1307
1308
1309
1310         /*
1311          * Draw "x" for Sequence Errors and "m" for Marks
1312          */
1313         /* Draw the labels Fwd and Rev */
1314         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd", sizeof(label_string));
1315         pango_layout_set_text(layout, label_string, -1);
1316         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1317 #if GTK_CHECK_VERSION(2,22,0)
1318         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1319 #else
1320         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1321 #endif
1322         cairo_move_to (cr,
1323                 user_data->dlg.dialog_graph.surface_width - right_x_border + 33 - lwidth,
1324                 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3);
1325         pango_cairo_show_layout (cr, layout);
1326         cairo_destroy (cr);
1327         cr = NULL;
1328
1329         g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev", sizeof(label_string));
1330         pango_layout_set_text(layout, label_string, -1);
1331         pango_layout_get_pixel_size(layout, &lwidth, NULL);
1332 #if GTK_CHECK_VERSION(2,22,0)
1333         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1334 #else
1335         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1336 #endif
1337         cairo_move_to (cr,
1338                 user_data->dlg.dialog_graph.surface_width - right_x_border + 33 - lwidth,
1339                 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3 + 9);
1340         pango_cairo_show_layout (cr, layout);
1341         cairo_destroy (cr);
1342         cr = NULL;
1343
1344         /* Draw the marks */
1345         for (i = MAX_GRAPHS-1; i >= 0; i--) {
1346                 guint32 interval;
1347                 guint32 x_pos/*, prev_x_pos*/;
1348
1349                 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1350                 if (!user_data->dlg.dialog_graph.graph[i].display) {
1351                         continue;
1352                 }
1353                 /* initialize prev x/y to the low left corner of the graph */
1354                 /*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;*/
1355
1356                 for (interval = first_interval+user_data->dlg.dialog_graph.interval;
1357                      interval <= last_interval;
1358                      interval += user_data->dlg.dialog_graph.interval) {
1359                         x_pos = draw_width - 1
1360                                 - (user_data->dlg.dialog_graph.pixels_per_tick
1361                                    * ((last_interval-interval)/user_data->dlg.dialog_graph.interval + 1))
1362                                 + left_x_border;
1363
1364                         if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags &
1365                                 (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER|STAT_FLAG_DUP_PKT)) {
1366                                 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags &
1367                                         (STAT_FLAG_WRONG_SEQ|STAT_FLAG_DUP_PKT)) {
1368                                         g_strlcpy(label_string, "x", sizeof(label_string));
1369                                 } else {
1370                                         g_strlcpy(label_string, "m", sizeof(label_string));
1371                                 }
1372
1373                                 pango_layout_set_text(layout, label_string, -1);
1374                                 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1375 #if GTK_CHECK_VERSION(2,22,0)
1376                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1377 #else
1378                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1379 #endif
1380                                 cairo_move_to (cr,
1381                                         x_pos - 1 - lwidth/2,
1382                                         user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3 + 7*(i/2));
1383                                 pango_cairo_show_layout (cr, layout);
1384                                 cairo_destroy (cr);
1385                                 cr = NULL;
1386
1387                         }
1388
1389                         /*prev_x_pos = x_pos;*/
1390                 }
1391         }
1392
1393         g_object_unref(G_OBJECT(layout));
1394
1395         /*
1396          * Loop over all graphs and draw them
1397          */
1398         for (i = MAX_GRAPHS-1; i >= 0; i--) {
1399                 guint32 interval;
1400                 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1401                 if (! user_data->dlg.dialog_graph.graph[i].display) {
1402                         continue;
1403                 }
1404                 /* initialize prev x/y to the low left corner of the graph */
1405                 /*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;*/
1406                 prev_y_pos = draw_height-1+top_y_border;
1407
1408                 for (interval = first_interval+user_data->dlg.dialog_graph.interval;
1409                      interval <= last_interval;
1410                      interval+=user_data->dlg.dialog_graph.interval) {
1411                         guint32 val;
1412                         x_pos = draw_width - 1
1413                                 - (user_data->dlg.dialog_graph.pixels_per_tick
1414                                    * ((last_interval-interval)/user_data->dlg.dialog_graph.interval+1))
1415                                 + left_x_border;
1416                         val = get_it_value(&user_data->dlg.dialog_graph.graph[i],
1417                                            interval/user_data->dlg.dialog_graph.interval);
1418                         if (val>max_y) {
1419                                 y_pos = 0;
1420                         } else {
1421                                 y_pos = draw_height - 1 - (val*draw_height)/max_y + top_y_border;
1422                         }
1423
1424                         /* don't need to draw anything if the segment
1425                          * is entirely above the top of the graph
1426                          */
1427                         if ( (prev_y_pos == 0) && (y_pos == 0) ) {
1428                                 prev_y_pos = y_pos;
1429                                 /*prev_x_pos = x_pos;*/
1430                                 continue;
1431                         }
1432
1433                         if (val) {
1434 #if GTK_CHECK_VERSION(2,22,0)
1435                                 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1436 #else
1437                                 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1438 #endif
1439                                 gdk_cairo_set_source_rgba (cr, &user_data->dlg.dialog_graph.graph[i].rgba_color);
1440                                 cairo_set_line_width (cr, 1.0);
1441                                 cairo_move_to(cr, x_pos + 0.5, draw_height - 1 + top_y_border + 0.5);
1442                                 cairo_line_to(cr, x_pos + 0.5, y_pos + 0.5);
1443                                 cairo_stroke(cr);
1444                                 cairo_destroy(cr);
1445                         }
1446
1447                         prev_y_pos = y_pos;
1448                         /*prev_x_pos = x_pos;*/
1449                 }
1450         }
1451
1452         cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1453
1454 #if GTK_CHECK_VERSION(2,22,0)
1455         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1456 #else
1457         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1458 #endif
1459         cairo_rectangle (cr, 0, 0,
1460                          user_data->dlg.dialog_graph.surface_width,
1461                          user_data->dlg.dialog_graph.surface_height);
1462         cairo_fill (cr);
1463
1464         cairo_destroy (cr);
1465
1466         /* update the scrollbar */
1467         gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment,
1468                                  (gfloat) user_data->dlg.dialog_graph.max_interval);
1469         gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment,
1470                                           (gfloat) ((last_interval - first_interval)/10));
1471         gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment,
1472                                           (gfloat) (last_interval - first_interval));
1473         if (((last_interval-first_interval)*100) < user_data->dlg.dialog_graph.max_interval) {
1474                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment,
1475                                              (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1476         } else {
1477                 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment,
1478                                              (gfloat) (last_interval - first_interval));
1479         }
1480         gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment,
1481                                  last_interval
1482                                  - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1483         gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1484         gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1485
1486 }
1487
1488 /****************************************************************************/
1489 static void
1490 dialog_graph_redraw(user_data_t* user_data)
1491 {
1492         user_data->dlg.dialog_graph.needs_redraw = TRUE;
1493         dialog_graph_draw(user_data);
1494 }
1495
1496 /****************************************************************************/
1497 static void
1498 quit(GtkWidget *widget _U_, user_data_t *user_data)
1499 {
1500         GtkWidget      *bt_save      = (GtkWidget *)g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1501         surface_info_t *surface_info = (surface_info_t *)g_object_get_data(G_OBJECT(bt_save), "surface-info");
1502
1503         g_free(surface_info);
1504         user_data->dlg.dialog_graph.window = NULL;
1505 }
1506
1507 /****************************************************************************/
1508 #if GTK_CHECK_VERSION(3,0,0)
1509 static gboolean
1510 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
1511 {
1512         user_data_t   *user_data = (user_data_t *)data;
1513         GtkAllocation  allocation;
1514
1515         gtk_widget_get_allocation (widget, &allocation);
1516
1517         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1518         cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
1519         cairo_fill (cr);
1520
1521         return FALSE;
1522 }
1523 #else
1524 static gint
1525 expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1526 {
1527         user_data_t *user_data = (user_data_t *)data;
1528         cairo_t     *cr        = gdk_cairo_create (gtk_widget_get_window(widget));
1529
1530
1531 #if GTK_CHECK_VERSION(2,22,0)
1532         cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1533 #else
1534         gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1535 #endif
1536         cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1537         cairo_fill (cr);
1538
1539         cairo_destroy (cr);
1540
1541         return FALSE;
1542 }
1543 #endif
1544
1545 /****************************************************************************/
1546 static gint
1547 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1548 {
1549         user_data_t    *user_data    = (user_data_t *)data;
1550         GtkWidget      *bt_save;
1551         GtkAllocation   widget_alloc;
1552         cairo_t        *cr;
1553 #if GTK_CHECK_VERSION(2,22,0)
1554         surface_info_t *surface_info = g_new(surface_info_t, 1);
1555 #endif
1556
1557         if (!user_data) {
1558                 exit(10);
1559         }
1560
1561 #if GTK_CHECK_VERSION(2,22,0)
1562         if (user_data->dlg.dialog_graph.surface) {
1563                 g_object_unref(user_data->dlg.dialog_graph.surface);
1564                 user_data->dlg.dialog_graph.surface = NULL;
1565         }
1566         gtk_widget_get_allocation(widget, &widget_alloc);
1567         user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1568                         CAIRO_CONTENT_COLOR,
1569                         widget_alloc.width,
1570                         widget_alloc.height);
1571 #else
1572         if (user_data->dlg.dialog_graph.pixmap) {
1573                 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1574                 user_data->dlg.dialog_graph.pixmap = NULL;
1575         }
1576
1577         gtk_widget_get_allocation(widget, &widget_alloc);
1578         user_data->dlg.dialog_graph.pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
1579                                                             widget_alloc.width,
1580                                                             widget_alloc.height,
1581                                                             -1);
1582 #endif
1583         user_data->dlg.dialog_graph.surface_width = widget_alloc.width;
1584         user_data->dlg.dialog_graph.surface_height = widget_alloc.height;
1585
1586         bt_save = (GtkWidget *)g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1587 #if GTK_CHECK_VERSION(2,22,0)
1588         surface_info->surface = user_data->dlg.dialog_graph.surface;
1589         surface_info->width   = widget_alloc.width;
1590         surface_info->height  = widget_alloc.height;
1591         g_object_set_data(G_OBJECT(bt_save), "surface-info", surface_info);
1592         gtk_widget_set_sensitive(bt_save, TRUE);
1593
1594         cr = cairo_create (user_data->dlg.dialog_graph.surface);
1595 #else
1596         g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1597         gtk_widget_set_sensitive(bt_save, TRUE);
1598
1599         cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1600 #endif
1601         cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1602         cairo_set_source_rgb (cr, 1, 1, 1);
1603         cairo_fill (cr);
1604         cairo_destroy (cr);
1605
1606         dialog_graph_redraw(user_data);
1607         return TRUE;
1608 }
1609
1610 /****************************************************************************/
1611 static gint
1612 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1613 {
1614         user_data_t *user_data = (user_data_t *)data;
1615         guint32      mi;
1616
1617         mi = (guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment)
1618                         + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1619         if (user_data->dlg.dialog_graph.last_interval == mi) {
1620                 return TRUE;
1621         }
1622         if ( (user_data->dlg.dialog_graph.last_interval == 0xffffffff)
1623             &&  (mi == user_data->dlg.dialog_graph.max_interval) ) {
1624                 return TRUE;
1625         }
1626
1627         user_data->dlg.dialog_graph.last_interval =
1628                 (mi / user_data->dlg.dialog_graph.interval) * user_data->dlg.dialog_graph.interval;
1629
1630         dialog_graph_redraw(user_data);
1631         return TRUE;
1632 }
1633
1634 /****************************************************************************/
1635 static void
1636 create_draw_area(user_data_t* user_data, GtkWidget *box)
1637 {
1638         user_data->dlg.dialog_graph.draw_area = gtk_drawing_area_new();
1639         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1640
1641         gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area,
1642                                     user_data->dlg.dialog_graph.surface_width,
1643                                     user_data->dlg.dialog_graph.surface_height);
1644
1645         /* signals needed to handle backing pixmap */
1646 #if GTK_CHECK_VERSION(3,0,0)
1647         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
1648 #else
1649         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
1650 #endif
1651         g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1652
1653         gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1654         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1655
1656         /* create the associated scrollbar */
1657         user_data->dlg.dialog_graph.scrollbar_adjustment = (GtkAdjustment *)gtk_adjustment_new(0, 0, 0, 0, 0, 0);
1658         user_data->dlg.dialog_graph.scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL,
1659                                                                   user_data->dlg.dialog_graph.scrollbar_adjustment);
1660         gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1661         gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1662         g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment,
1663                          "value_changed",
1664                          G_CALLBACK(scrollbar_changed),
1665                          user_data);
1666 }
1667
1668 /****************************************************************************/
1669 static void
1670 disable_graph(dialog_graph_graph_t *dgg)
1671 {
1672         if (dgg->display) {
1673                 dgg->display = FALSE;
1674                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1675                                              FALSE);
1676         }
1677 }
1678
1679 /****************************************************************************/
1680 static gint
1681 filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1682 {
1683         /* this graph is not active, just update display and redraw */
1684         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))) {
1685                 disable_graph(dgg);
1686                 dialog_graph_redraw(dgg->ud);
1687                 return 0;
1688         }
1689
1690         enable_graph(dgg);
1691         cf_retap_packets(&cfile);
1692         dialog_graph_redraw(dgg->ud);
1693
1694         return 0;
1695 }
1696
1697 /****************************************************************************/
1698 static void
1699 create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *vbox, int num)
1700 {
1701         GtkWidget *hbox;
1702         GtkWidget *label;
1703         char       str[256];
1704
1705         hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1706         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1707         gtk_widget_show(hbox);
1708
1709         g_snprintf(str, sizeof(str), "Graph %d", num);
1710         dgg->display_button = gtk_toggle_button_new_with_label(str);
1711         gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1712         gtk_widget_show(dgg->display_button);
1713         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1714         g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1715
1716         label = gtk_label_new(dgg->title);
1717         gtk_widget_show(label);
1718         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1719
1720 #if GTK_CHECK_VERSION(3,0,0)
1721         gtk_widget_override_color(label, GTK_STATE_FLAG_NORMAL, &dgg->rgba_color);
1722         gtk_widget_override_color(label, GTK_STATE_FLAG_ACTIVE, &dgg->rgba_color);
1723         gtk_widget_override_color(label, GTK_STATE_FLAG_PRELIGHT, &dgg->rgba_color);
1724         gtk_widget_override_color(label, GTK_STATE_FLAG_SELECTED, &dgg->rgba_color);
1725         gtk_widget_override_color(label, GTK_STATE_FLAG_INSENSITIVE, &dgg->rgba_color);
1726 #else
1727         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1728         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1729         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1730         gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1731         gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1732 #endif
1733         return;
1734 }
1735
1736 /****************************************************************************/
1737 static void
1738 create_filter_area(user_data_t* user_data, GtkWidget *box)
1739 {
1740         GtkWidget *frame;
1741         GtkWidget *vbox;
1742         int        i;
1743         GtkWidget *label;
1744
1745         frame = gtk_frame_new("Graphs");
1746         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
1747         gtk_widget_show(frame);
1748
1749         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 1, FALSE);
1750         gtk_container_add(GTK_CONTAINER(frame), vbox);
1751         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1752         gtk_widget_show(vbox);
1753
1754         for (i = 0; i < MAX_GRAPHS; i++) {
1755                 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1756         }
1757
1758         label = gtk_label_new("Label:    x = Wrong Seq. number      m = Mark set");
1759         gtk_widget_show(label);
1760         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1761
1762         return;
1763 }
1764
1765 /****************************************************************************/
1766 static void
1767 yscale_select(GtkWidget *item, gpointer key)
1768 {
1769         int          i;
1770         user_data_t *user_data;
1771
1772         user_data = (user_data_t *)key;
1773         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1774
1775         user_data->dlg.dialog_graph.max_y_units = yscale_max[i];
1776         dialog_graph_redraw(user_data);
1777 }
1778
1779 /****************************************************************************/
1780 static void
1781 pixels_per_tick_select(GtkWidget *item, gpointer key)
1782 {
1783         int          i;
1784         user_data_t *user_data;
1785
1786         user_data = (user_data_t *)key;
1787         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1788
1789         user_data->dlg.dialog_graph.pixels_per_tick = pixels_per_tick[i];
1790         dialog_graph_redraw(user_data);
1791 }
1792
1793 /****************************************************************************/
1794 static void
1795 tick_interval_select(GtkWidget *item, gpointer key)
1796 {
1797         int i;
1798         user_data_t *user_data;
1799
1800         user_data = (user_data_t *)key;
1801         i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1802
1803         user_data->dlg.dialog_graph.interval = tick_interval_values[i];
1804         cf_retap_packets(&cfile);
1805         dialog_graph_redraw(user_data);
1806 }
1807
1808 /****************************************************************************/
1809 static GtkWidget *
1810 create_yscale_max_menu_items(user_data_t* user_data)
1811 {
1812         char       str[15];
1813         GtkWidget *combo_box;
1814         int        i;
1815
1816         combo_box = gtk_combo_box_text_new();
1817
1818         for (i = 0; i < MAX_YSCALE; i++) {
1819                 if (yscale_max[i] == AUTO_MAX_YSCALE) {
1820                         g_strlcpy(str, "Auto", sizeof(str));
1821                 } else if (yscale_max[i] < 1000000) {
1822                         g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1823                 } else {
1824                         g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1825                 }
1826                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1827         }
1828         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1829         g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1830
1831         return combo_box;
1832 }
1833
1834 /****************************************************************************/
1835 static GtkWidget *
1836 create_pixels_per_tick_menu_items(user_data_t *user_data)
1837 {
1838         char       str[5];
1839         GtkWidget *combo_box;
1840         int        i;
1841
1842         combo_box = gtk_combo_box_text_new();
1843
1844         for (i = 0; i < MAX_PIXELS_PER_TICK; i++) {
1845                 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1846                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1847         }
1848         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1849
1850         g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1851
1852         return combo_box;
1853 }
1854
1855 /****************************************************************************/
1856 static GtkWidget *
1857 create_tick_interval_menu_items(user_data_t *user_data)
1858 {
1859         GtkWidget *combo_box;
1860         char       str[15];
1861         int        i;
1862
1863         combo_box = gtk_combo_box_text_new();
1864
1865         for (i = 0; i < MAX_TICK_VALUES; i++) {
1866                 if (tick_interval_values[i] >= 1000) {
1867                         g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1868                 } else if (tick_interval_values[i] >= 100) {
1869                         g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1870                 } else if (tick_interval_values[i] >= 10) {
1871                         g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1872                 } else {
1873                         g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1874                 }
1875                  gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1876         }
1877         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1878         g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1879
1880         return combo_box;
1881 }
1882
1883 /****************************************************************************/
1884 static void
1885 create_ctrl_menu(user_data_t* user_data, GtkWidget *vbox, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1886 {
1887         GtkWidget *hbox;
1888         GtkWidget *label;
1889         GtkWidget *combo_box;
1890
1891         hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1892         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1893         gtk_widget_show(hbox);
1894
1895         label = gtk_label_new(name);
1896         gtk_widget_show(label);
1897         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1898
1899         combo_box = (*func)(user_data);
1900         gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1901         gtk_widget_show(combo_box);
1902 }
1903
1904 /****************************************************************************/
1905 static void
1906 create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1907 {
1908         GtkWidget *frame_vbox;
1909         GtkWidget *frame;
1910         GtkWidget *vbox;
1911
1912         frame_vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1913         gtk_box_pack_start(GTK_BOX(box), frame_vbox, TRUE, TRUE, 0);
1914         gtk_widget_show(frame_vbox);
1915
1916         frame = gtk_frame_new("X Axis");
1917         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1918         gtk_widget_show(frame);
1919
1920         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1921         gtk_container_add(GTK_CONTAINER(frame), vbox);
1922         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1923         gtk_widget_show(vbox);
1924
1925         create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1926         create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1927
1928         frame = gtk_frame_new("Y Axis");
1929         gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1930         gtk_widget_show(frame);
1931
1932         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1933         gtk_container_add(GTK_CONTAINER(frame), vbox);
1934         gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1935         gtk_widget_show(vbox);
1936
1937         create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1938
1939         return;
1940 }
1941
1942 /****************************************************************************/
1943 static void
1944 dialog_graph_init_window(user_data_t* user_data)
1945 {
1946         GtkWidget *vbox;
1947         GtkWidget *hbox;
1948         GtkWidget *bt_close;
1949         GtkWidget *bt_save;
1950
1951         /* create the main window */
1952         user_data->dlg.dialog_graph.window = dlg_window_new("I/O Graphs");   /* transient_for top_level */
1953
1954         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1955         gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1956         gtk_widget_show(vbox);
1957
1958         create_draw_area(user_data, vbox);
1959
1960         hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1961         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1962         gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1963         gtk_widget_show(hbox);
1964
1965         create_filter_area(user_data, hbox);
1966         create_ctrl_area(user_data, hbox);
1967
1968         dialog_graph_set_title(user_data);
1969
1970         hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1971         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1972         gtk_widget_show(hbox);
1973
1974         bt_close = (GtkWidget *)g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1975         window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1976
1977         bt_save = (GtkWidget *)g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1978         gtk_widget_set_sensitive(bt_save, FALSE);
1979         gtk_widget_set_tooltip_text(bt_save, "Save the displayed graph to a file");
1980         g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1981         g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1982
1983         g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1984
1985         gtk_widget_show(user_data->dlg.dialog_graph.window);
1986         window_present(user_data->dlg.dialog_graph.window);
1987
1988 }
1989
1990
1991 /****************************************************************************/
1992 static void
1993 on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1994 {
1995         if (user_data->dlg.dialog_graph.window != NULL) {
1996                 /* There's already a graph window; reactivate it. */
1997                 reactivate_window(user_data->dlg.dialog_graph.window);
1998                 return;
1999         }
2000
2001         dialog_graph_init_window(user_data);
2002
2003 }
2004
2005 /****************************************************************************/
2006
2007 static void
2008 on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
2009 {
2010         GtkTreeIter       iter;
2011         GtkTreeModel     *model;
2012         GtkTreeSelection *selection;
2013         guint             fnumber;
2014
2015         selection = user_data->dlg.selected_list_sel;
2016
2017         if (selection == NULL)
2018                 return;
2019
2020         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2021                 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
2022                 cf_goto_frame(&cfile, fnumber);
2023         }
2024
2025 }
2026
2027 static void draw_stat(user_data_t *user_data);
2028
2029 /****************************************************************************/
2030 /* re-dissects all packets */
2031 static void
2032 on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2033 {
2034         GString *error_string;
2035
2036         /* remove tap listener */
2037         remove_tap_listener(user_data);
2038
2039         /* register tap listener */
2040         error_string = register_tap_listener("rtp", user_data, NULL, 0,
2041                 rtp_reset, rtp_packet, rtp_draw);
2042         if (error_string != NULL) {
2043                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
2044                         g_string_free(error_string, TRUE);
2045                 return;
2046         }
2047
2048         /* retap all packets */
2049         cf_retap_packets(&cfile);
2050
2051         /* draw statistics info */
2052         draw_stat(user_data);
2053
2054 }
2055
2056 #ifdef HAVE_LIBPORTAUDIO
2057 /****************************************************************************/
2058 static void
2059 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
2060 {
2061         rtp_player_init(NULL);
2062 }
2063 #endif /* HAVE_LIBPORTAUDIO */
2064
2065 static void
2066 on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
2067 {
2068         GtkTreeIter       iter;
2069         GtkTreeModel     *model;
2070         gchar            *text;
2071         GtkTreeSelection *selection;
2072         GtkTreePath      *path;
2073
2074         selection = user_data->dlg.selected_list_sel;
2075
2076         if (selection == NULL)
2077                 return;
2078
2079 try_again:
2080         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2081                 while (gtk_tree_model_iter_next (model, &iter)) {
2082                         gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
2083                         if (strcmp(text, OK_TEXT) != 0) {
2084                                 gtk_tree_selection_select_iter (selection, &iter);
2085                                 path = gtk_tree_model_get_path(model, &iter);
2086                                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
2087                                                 path,
2088                                                 NULL, FALSE, 0, 0);
2089                                 gtk_tree_path_free(path);
2090                                 g_free (text);
2091                                 return;
2092                         }
2093                         g_free (text);
2094                 }
2095                 /* wrap around */
2096                 if (user_data->dlg.number_of_nok>1) {
2097                         /* Get the first iter and select it before starting over */
2098                         gtk_tree_model_get_iter_first(model, &iter);
2099                         gtk_tree_selection_select_iter (selection, &iter);
2100                         goto try_again;
2101                 }
2102         }
2103 }
2104
2105
2106 /****************************************************************************/
2107 /* when we want to save the information */
2108 static gboolean
2109 save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
2110 {
2111         gchar        *g_dest;
2112         GtkWidget    *rev, *forw, *both;
2113         user_data_t  *user_data;
2114
2115         GtkListStore *store;
2116         GtkTreeIter   iter;
2117         GtkTreeModel *model;
2118         gboolean      more_items = TRUE;
2119
2120         /* To Hold data from the list row */
2121         guint32       packet;     /* Packet                     */
2122         guint16       sequence;   /* Sequence                   */
2123         guint32       timestamp;  /* timestamp                  */
2124         gfloat        delta;      /* Delta(ms)                  */
2125         gfloat        jitter;     /* Jitter(ms)                 */
2126         gfloat        skew;       /* Skew(ms)                   */
2127         gfloat        ipbw;       /* IP BW(kbps)                */
2128         gboolean      marker;     /* Marker                     */
2129         char         *status_str; /* Status                     */
2130         char         *date_str;   /* Date                       */
2131         guint         length;     /* Length                     */
2132
2133         FILE         *fp;
2134         int           j;
2135
2136         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2137
2138         /* Perhaps the user specified a directory instead of a file.
2139          * Check whether they did.
2140          */
2141         if (test_for_directory(g_dest) == EISDIR) {
2142                 /* It's a directory - set the file selection box to display it. */
2143                 set_last_open_dir(g_dest);
2144                 g_free(g_dest);
2145                 file_selection_set_current_folder((GtkWidget *)fc, get_last_open_dir());
2146                 gtk_file_chooser_set_current_name((GtkFileChooser *)fc, "");
2147                 return FALSE; /* run the dialog again */
2148         }
2149
2150         rev  = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2151         forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
2152         both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
2153         user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
2154
2155         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw))
2156             || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2157                 fp = ws_fopen(g_dest, "w");
2158                 if (fp == NULL) {
2159                         open_failure_alert_box(g_dest, errno, TRUE);
2160                         g_free(g_dest);
2161                         return TRUE; /* we're done */
2162                 }
2163
2164                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2165                         fprintf(fp, "Forward\n");
2166                         if (ferror(fp)) {
2167                                 write_failure_alert_box(g_dest, errno);
2168                                 fclose(fp);
2169                                 g_free(g_dest);
2170                                 return TRUE; /* we're done */
2171                         }
2172                 }
2173
2174                 for (j = 0; j < NUM_COLS; j++) {
2175                         if (j == 0) {
2176                                 fprintf(fp, "\"%s\"", titles[j]);
2177                         } else {
2178                                 fprintf(fp, ",\"%s\"", titles[j]);
2179                         }
2180                 }
2181                 fprintf(fp, "\n");
2182                 if (ferror(fp)) {
2183                         write_failure_alert_box(g_dest, errno);
2184                         fclose(fp);
2185                         g_free(g_dest);
2186                         return TRUE;
2187                 }
2188                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2189                 store = GTK_LIST_STORE(model);
2190                 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2191
2192                          while (more_items) {
2193                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2194                                                     PACKET_COLUMN,    &packet,
2195                                                     SEQUENCE_COLUMN,  &sequence,
2196                                                     TIMESTAMP_COLUMN, &timestamp,
2197                                                     DELTA_COLUMN,     &delta,
2198                                                     JITTER_COLUMN,    &jitter,
2199                                                     SKEW_COLUMN,      &skew,
2200                                                     IPBW_COLUMN,      &ipbw,
2201                                                     MARKER_COLUMN,    &marker,
2202                                                     STATUS_COLUMN,    &status_str,
2203                                                     DATE_COLUMN,      &date_str,
2204                                                     LENGTH_COLUMN,    &length,
2205                                          -1);
2206                                  fprintf(fp, "\"%u\"",    packet);
2207                                  fprintf(fp, ",\"%u\"",   sequence);
2208                                  fprintf(fp, ",\"%u\"",   timestamp);
2209                                  fprintf(fp, ",\"%.2f\"", delta);
2210                                  fprintf(fp, ",\"%.2f\"", jitter);
2211                                  fprintf(fp, ",\"%.2f\"", skew);
2212                                  fprintf(fp, ",\"%.2f\"", ipbw);
2213                                  fprintf(fp, ",\"%s\"",   marker? "SET" : "");
2214                                  fprintf(fp, ",\"%s\"",   status_str);
2215                                  fprintf(fp, ",\"%s\"",   date_str);
2216                                  fprintf(fp, ",\"%u\"",   length);
2217                                  fprintf(fp,"\n");
2218                                  g_free(status_str);
2219                                  g_free(date_str);
2220                                  if (ferror(fp)) {
2221                                          write_failure_alert_box(g_dest, errno);
2222                                          fclose(fp);
2223                                          g_free(g_dest);
2224                                          return TRUE; /* we're done */
2225                                  }
2226
2227                                  more_items = gtk_tree_model_iter_next (model, &iter);
2228                          }
2229                  }
2230
2231                 if (fclose(fp) == EOF) {
2232                         write_failure_alert_box(g_dest, errno);
2233                         g_free(g_dest);
2234                         return TRUE; /* we're done */
2235                 }
2236         }
2237
2238         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev))
2239             || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2240
2241                 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2242                         fp = ws_fopen(g_dest, "a");
2243                         if (fp == NULL) {
2244                                 open_failure_alert_box(g_dest, errno, TRUE);
2245                                 g_free(g_dest);
2246                                 return TRUE; /* we're done */
2247                         }
2248                         fprintf(fp, "\nReverse\n");
2249                         if (ferror(fp)) {
2250                                 write_failure_alert_box(g_dest, errno);
2251                                 fclose(fp);
2252                                 g_free(g_dest);
2253                                 return TRUE; /* we're done */
2254                         }
2255                 } else {
2256                         fp = ws_fopen(g_dest, "w");
2257                         if (fp == NULL) {
2258                                 open_failure_alert_box(g_dest, errno, TRUE);
2259                                 g_free(g_dest);
2260                                 return TRUE; /* we're done */
2261                         }
2262                 }
2263                 for (j = 0; j < NUM_COLS; j++) {
2264                         if (j == 0) {
2265                                 fprintf(fp, "\"%s\"", titles[j]);
2266                         } else {
2267                                 fprintf(fp, ",\"%s\"", titles[j]);
2268                         }
2269                 }
2270                 fprintf(fp, "\n");
2271                 if (ferror(fp)) {
2272                         write_failure_alert_box(g_dest, errno);
2273                         fclose(fp);
2274                         g_free(g_dest);
2275                         return TRUE; /* we're done */
2276                 }
2277                 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2278                 store = GTK_LIST_STORE(model);
2279                 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2280
2281                         more_items = TRUE;
2282
2283                          while (more_items) {
2284                                  gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2285                                          PACKET_COLUMN,    &packet,
2286                                          SEQUENCE_COLUMN,  &sequence,
2287                                          TIMESTAMP_COLUMN, &timestamp,
2288                                          DELTA_COLUMN,     &delta,
2289                                          JITTER_COLUMN,    &jitter,
2290                                          SKEW_COLUMN,      &skew,
2291                                          IPBW_COLUMN,      &ipbw,
2292                                          MARKER_COLUMN,    &marker,
2293                                          STATUS_COLUMN,    &status_str,
2294                                          DATE_COLUMN,      &date_str,
2295                                          LENGTH_COLUMN,    &length,
2296                                          -1);
2297                                  fprintf(fp, "\"%u\"",    packet);
2298                                  fprintf(fp, ",\"%u\"",   sequence);
2299                                  fprintf(fp, ",\"%u\"",   timestamp);
2300                                  fprintf(fp, ",\"%.2f\"", delta);
2301                                  fprintf(fp, ",\"%.2f\"", jitter);
2302                                  fprintf(fp, ",\"%.2f\"", skew);
2303                                  fprintf(fp, ",\"%.2f\"", ipbw);
2304                                  fprintf(fp, ",\"%s\"",   marker? "SET" : "");
2305                                  fprintf(fp, ",\"%s\"",   status_str);
2306                                  fprintf(fp, ",\"%s\"",   date_str);
2307                                  fprintf(fp, ",\"%u\"",   length);
2308                                  fprintf(fp, "\n");
2309                                  g_free(status_str);
2310                                  g_free(date_str);
2311                                  if (ferror(fp)) {
2312                                          write_failure_alert_box(g_dest, errno);
2313                                          fclose(fp);
2314                                          g_free(g_dest);
2315                                          return TRUE; /* we're done */
2316                                  }
2317
2318                                  more_items = gtk_tree_model_iter_next (model, &iter);
2319                          }
2320                  }
2321                 if (fclose(fp) == EOF) {
2322                         write_failure_alert_box(g_dest, errno);
2323                         g_free(g_dest);
2324                         return TRUE; /* we're done */
2325                 }
2326         }
2327
2328         g_free(g_dest);
2329         return TRUE; /* we're done */
2330 }
2331
2332 static void
2333 save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2334 {
2335         user_data->dlg.save_csv_as_w = NULL;
2336 }
2337
2338 /* when the user wants to save the csv information in a file */
2339 static void
2340 save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2341 {
2342         GtkWidget *vertb;
2343         GtkWidget *grid1;
2344         GtkWidget *label_format;
2345         GtkWidget *channels_label;
2346         GtkWidget *forward_rb;
2347         GtkWidget *reversed_rb;
2348         GtkWidget *both_rb;
2349
2350 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2351         if (user_data->dlg.save_csv_as_w != NULL) {
2352                 /* There's already a Save CSV info dialog box; reactivate it. */
2353                 reactivate_window(user_data->dlg.save_csv_as_w);
2354                 return;
2355         }
2356 #endif
2357         user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2358                                                                    GTK_WINDOW(user_data->dlg.notebook),
2359                                                                    GTK_FILE_CHOOSER_ACTION_SAVE,
2360                                                                    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2361                                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2362                                                                    NULL);
2363         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2364
2365         /* Container for each row of widgets */
2366         vertb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2367         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2368         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2369         gtk_widget_show (vertb);
2370
2371         grid1 = ws_gtk_grid_new ();
2372         gtk_widget_show (grid1);
2373         gtk_box_pack_start (GTK_BOX (vertb), grid1, FALSE, FALSE, 0);
2374         gtk_container_set_border_width (GTK_CONTAINER (grid1), 10);
2375         ws_gtk_grid_set_row_spacing (GTK_GRID (grid1), 20);
2376
2377         label_format = gtk_label_new ("Format: Comma Separated Values");
2378         gtk_widget_show (label_format);
2379         ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 3, 1,
2380                                      (GtkAttachOptions) (GTK_FILL),
2381                                      (GtkAttachOptions) (0), 0, 0);
2382
2383         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2384
2385
2386         channels_label = gtk_label_new ("Channels:    ");
2387         gtk_widget_show (channels_label);
2388         ws_gtk_grid_attach_extended (GTK_GRID (grid1), channels_label, 0, 1, 1, 1,
2389                                      (GtkAttachOptions) (GTK_FILL),
2390                                      (GtkAttachOptions) (0), 0, 0);
2391         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2392
2393         forward_rb = gtk_radio_button_new_with_label (NULL, "forward  ");
2394         gtk_widget_show (forward_rb);
2395         ws_gtk_grid_attach_extended (GTK_GRID (grid1), forward_rb, 1, 1, 1, 1,
2396                                      (GtkAttachOptions) (GTK_FILL),
2397                                      (GtkAttachOptions) (0), 0, 0);
2398
2399         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
2400         gtk_widget_show (reversed_rb);
2401         ws_gtk_grid_attach_extended (GTK_GRID (grid1), reversed_rb, 2, 1, 1, 1,
2402                                      (GtkAttachOptions) (GTK_FILL),
2403                                      (GtkAttachOptions) (0), 0, 0);
2404
2405         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2406         gtk_widget_show (both_rb);
2407         ws_gtk_grid_attach_extended (GTK_GRID (grid1), both_rb, 3, 1, 1, 1,
2408                                      (GtkAttachOptions) (GTK_FILL),
2409                                      (GtkAttachOptions) (0), 0, 0);
2410
2411         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2412
2413         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2414         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2415         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2416         g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2417
2418         g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2419                 G_CALLBACK(window_delete_event_cb), NULL);
2420         g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2421                 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2422
2423         gtk_widget_show(user_data->dlg.save_csv_as_w);
2424         window_present(user_data->dlg.save_csv_as_w);
2425
2426         /* "Run" the GtkFileChooserDialog.                                              */
2427         /* Upon exit: If "Accept" run the OK callback.                                  */
2428         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
2429         /*            Destroy the window.                                               */
2430         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
2431         /*      return with a TRUE status so that the dialog window will be destroyed.  */
2432         /*      Trying to re-run the dialog after popping up an alert box will not work */
2433         /*       since the user will not be able to dismiss the alert box.              */
2434         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
2435         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
2436         /*                                                                              */
2437         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
2438         /*            GtkFileChooserDialog.                                             */
2439         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2440                 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2441                         break; /* we're done */
2442                 }
2443         }
2444         window_destroy(user_data->dlg.save_csv_as_w);
2445 }
2446
2447
2448 /****************************************************************************/
2449 static void
2450 save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2451 {
2452         /* Note that we no longer have a Save voice info dialog box. */
2453         user_data->dlg.save_voice_as_w = NULL;
2454 }
2455
2456 /****************************************************************************/
2457 /* here we save it into a file that user specified */
2458 /* XXX what about endians here? could go something wrong? */
2459
2460 static gboolean
2461 copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2462 {
2463         FILE      *to_stream, *forw_stream, *rev_stream;
2464         size_t     fwritten, rwritten;
2465         int        f_rawvalue, r_rawvalue, rawvalue;
2466         gint16     sample;
2467         gchar      pd[4];
2468         guint32    f_write_silence  = 0;
2469         guint32    r_write_silence  = 0;
2470         progdlg_t *progbar;
2471         guint32    progbar_count, progbar_quantum;
2472         guint32    progbar_nextstep = 0;
2473         guint32    count            = 0;
2474         gboolean   stop_flag        = FALSE;
2475         size_t     nchars;
2476         gboolean   ret_val;
2477
2478         forw_stream = ws_fopen(user_data->f_tempname, "rb");
2479         if (forw_stream == NULL)
2480                 return FALSE;
2481         rev_stream = ws_fopen(user_data->r_tempname, "rb");
2482         if (rev_stream == NULL) {
2483                 fclose(forw_stream);
2484                 return FALSE;
2485         }
2486
2487         /* open file for saving */
2488         to_stream = ws_fopen(dest, "wb");
2489         if (to_stream == NULL) {
2490                 fclose(forw_stream);
2491                 fclose(rev_stream);
2492                 return FALSE;
2493         }
2494
2495         progbar = create_progress_dlg(top_level, "Saving voice in a file", dest, TRUE, &stop_flag);
2496
2497         if      (format == SAVE_AU_FORMAT) /* au format */
2498         {
2499                 /* First we write the .au header. XXX Hope this is endian independent */
2500                 /* the magic word 0x2e736e64 == .snd */
2501                 phton32(pd, 0x2e736e64);
2502                 nchars = fwrite(pd, 1, 4, to_stream);
2503                 if (nchars != 4)
2504                         goto copy_file_err;
2505                 /* header offset == 24 bytes */
2506                 phton32(pd, 24);
2507                 nchars = fwrite(pd, 1, 4, to_stream);
2508                 if (nchars != 4)
2509                         goto copy_file_err;
2510                 /* total length; it is permitted to set this to 0xffffffff */
2511                 phton32(pd, -1);
2512                 nchars = fwrite(pd, 1, 4, to_stream);
2513                 if (nchars != 4)
2514                         goto copy_file_err;
2515                 /* encoding format == 16-bit linear PCM */
2516                 phton32(pd, 3);
2517                 nchars = fwrite(pd, 1, 4, to_stream);
2518                 if (nchars != 4)
2519                         goto copy_file_err;
2520                 /* sample rate == 8000 Hz */
2521                 phton32(pd, 8000);
2522                 nchars = fwrite(pd, 1, 4, to_stream);
2523                 if (nchars != 4)
2524                         goto copy_file_err;
2525                 /* channels == 1 */
2526                 phton32(pd, 1);
2527                 nchars = fwrite(pd, 1, 4, to_stream);
2528                 if (nchars != 4)
2529                         goto copy_file_err;
2530
2531
2532                 switch (channels) {
2533                         /* only forward direction */
2534                         case SAVE_FORWARD_DIRECTION_MASK: {
2535                                 progbar_count = user_data->forward.saveinfo.count;
2536                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2537                                 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2538                                         if (stop_flag)
2539                                                 break;
2540                                         if ((count > progbar_nextstep) && (count <= progbar_count)) {
2541                                                 update_progress_dlg(progbar,
2542                                                         (gfloat) count/progbar_count, "Saving");
2543                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2544                                         }
2545                                         count++;
2546
2547                                         if (user_data->forward.statinfo.pt == PT_PCMU) {
2548                                                 sample = ulaw2linear((unsigned char)f_rawvalue);
2549                                                 phton16(pd, sample);
2550                                         }
2551                                         else if (user_data->forward.statinfo.pt == PT_PCMA) {
2552                                                 sample = alaw2linear((unsigned char)f_rawvalue);
2553                                                 phton16(pd, sample);
2554                                         }
2555                                         else{
2556                                                 goto copy_file_err;
2557                                         }
2558
2559                                         fwritten = fwrite(pd, 1, 2, to_stream);
2560                                         if (fwritten < 2) {
2561                                                 goto copy_file_err;
2562                                         }
2563                                 }
2564                                 break;
2565                         }
2566                         /* only reversed direction */
2567                         case SAVE_REVERSE_DIRECTION_MASK: {
2568                                 progbar_count = user_data->reversed.saveinfo.count;
2569                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2570                                 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2571                                         if (stop_flag)
2572                                                 break;
2573                                         if ((count > progbar_nextstep) && (count <= progbar_count)) {
2574                                                 update_progress_dlg(progbar,
2575                                                         (gfloat) count/progbar_count, "Saving");
2576                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2577                                         }
2578                                         count++;
2579
2580                                         if (user_data->reversed.statinfo.pt == PT_PCMU) {
2581                                                 sample = ulaw2linear((unsigned char)r_rawvalue);
2582                                                 phton16(pd, sample);
2583                                         }
2584                                         else if (user_data->reversed.statinfo.pt == PT_PCMA) {
2585                                                 sample = alaw2linear((unsigned char)r_rawvalue);
2586                                                 phton16(pd, sample);
2587                                         }
2588                                         else{
2589                                                 goto copy_file_err;
2590                                         }
2591
2592                                         rwritten = fwrite(pd, 1, 2, to_stream);
2593                                         if (rwritten < 2) {
2594                                                 goto copy_file_err;
2595                                         }
2596                                 }
2597                                 break;
2598                         }
2599                         /* both directions */
2600                         case SAVE_BOTH_DIRECTION_MASK: {
2601                                 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2602                                         (progbar_count = user_data->forward.saveinfo.count) :
2603                                         (progbar_count = user_data->reversed.saveinfo.count);
2604                                 progbar_quantum = progbar_count/100;
2605                                 /* since conversation in one way can start later than in the other one,
2606                                  * we have to write some silence information for one channel */
2607                                 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2608                                         f_write_silence = (guint32)
2609                                                 ((user_data->forward.statinfo.start_time
2610                                                   - user_data->reversed.statinfo.start_time)
2611                                                  * (8000/1000));
2612                                 }
2613                                 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2614                                         r_write_silence = (guint32)
2615                                                 ((user_data->reversed.statinfo.start_time
2616                                                   - user_data->forward.statinfo.start_time)
2617                                                  * (8000/1000));
2618                                 }
2619                                 for (;;) {
2620                                         if (stop_flag)
2621                                                 break;
2622                                         if ((count > progbar_nextstep) && (count <= progbar_count)) {
2623                                                 update_progress_dlg(progbar,
2624                                                         (gfloat) count/progbar_count, "Saving");
2625                                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2626                                         }
2627                                         count++;
2628                                         if (f_write_silence > 0) {
2629                                                 r_rawvalue = getc(rev_stream);
2630                                                 switch (user_data->forward.statinfo.reg_pt) {
2631                                                 case PT_PCMU:
2632                                                         f_rawvalue = SILENCE_PCMU;
2633                                                         break;
2634                                                 case PT_PCMA:
2635                                                         f_rawvalue = SILENCE_PCMA;
2636                                                         break;
2637                                                 default:
2638                                                         f_rawvalue = 0;
2639                                                         break;
2640                                                 }
2641                                                 f_write_silence--;
2642                                         }
2643                                         else if (r_write_silence > 0) {
2644                                                 f_rawvalue = getc(forw_stream);
2645                                                 switch (user_data->reversed.statinfo.reg_pt) {
2646                                                 case PT_PCMU:
2647                                                         r_rawvalue = SILENCE_PCMU;
2648                                                         break;
2649                                                 case PT_PCMA:
2650                                                         r_rawvalue = SILENCE_PCMA;
2651                                                         break;
2652                                                 default:
2653                                                         r_rawvalue = 0;
2654                                                         break;
2655                                                 }
2656                                                 r_write_silence--;
2657                                         }
2658                                         else {
2659                                                 f_rawvalue = getc(forw_stream);
2660                                                 r_rawvalue = getc(rev_stream);
2661                                         }
2662                                         if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2663                                                 break;
2664                                         if ((user_data->forward.statinfo.pt == PT_PCMU)
2665                                             && (user_data->reversed.statinfo.pt == PT_PCMU)) {
2666                                                 sample = (ulaw2linear((unsigned char)r_rawvalue)
2667                                                           + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2668                                                 phton16(pd, sample);
2669                                         }
2670                                         else if ((user_data->forward.statinfo.pt == PT_PCMA)
2671                                                  && (user_data->reversed.statinfo.pt == PT_PCMA)) {
2672                                                 sample = (alaw2linear((unsigned char)r_rawvalue)
2673                                                           + alaw2linear((unsigned char)f_rawvalue)) / 2;
2674                                                 phton16(pd, sample);
2675                                         }
2676                                         else
2677                                         {
2678                                                 goto copy_file_err;
2679                                         }
2680
2681
2682                                         rwritten = fwrite(pd, 1, 2, to_stream);
2683                                         if (rwritten < 2) {
2684                                                 goto copy_file_err;
2685                                         }
2686                                 }
2687                         }
2688                 }
2689         }
2690         else if (format == SAVE_RAW_FORMAT)     /* raw format */
2691         {
2692                 FILE *stream;
2693                 switch (channels) {
2694                         /* only forward direction */
2695                         case SAVE_FORWARD_DIRECTION_MASK: {
2696                                 progbar_count = user_data->forward.saveinfo.count;
2697                                 progbar_quantum = user_data->forward.saveinfo.count/100;
2698                                 stream = forw_stream;
2699                                 break;
2700                         }
2701                         /* only reversed direction */
2702                         case SAVE_REVERSE_DIRECTION_MASK: {
2703                                 progbar_count = user_data->reversed.saveinfo.count;
2704                                 progbar_quantum = user_data->reversed.saveinfo.count/100;
2705                                 stream = rev_stream;
2706                                 break;
2707                         }
2708                         default: {
2709                                 goto copy_file_err;
2710                         }
2711                 }
2712
2713
2714
2715                 /* XXX how do you just copy the file? */
2716                 while ((rawvalue = getc(stream)) != EOF) {
2717                         if (stop_flag)
2718                                 break;
2719                         if ((count > progbar_nextstep) && (count <= progbar_count)) {
2720                                 update_progress_dlg(progbar,
2721                                         (gfloat) count/progbar_count, "Saving");
2722                                 progbar_nextstep = progbar_nextstep + progbar_quantum;
2723                         }
2724                         count++;
2725
2726                         if (putc(rawvalue, to_stream) == EOF) {
2727                                 goto copy_file_err;
2728                         }
2729                 }
2730         }
2731
2732         ret_val = TRUE;
2733         goto copy_file_xit;
2734
2735 copy_file_err:
2736         ret_val = FALSE;
2737         goto copy_file_xit;
2738
2739 copy_file_xit:
2740         destroy_progress_dlg(progbar);
2741         fclose(forw_stream);
2742         fclose(rev_stream);
2743         fclose(to_stream);
2744         return ret_val;
2745 }
2746
2747
2748 /****************************************************************************/
2749 /* the user wants to save in a file */
2750 /* XXX support for different formats is currently commented out */
2751 static gboolean
2752 save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2753 {
2754         gchar       *g_dest;
2755         /*GtkWidget *wav, *sw;*/
2756         GtkWidget   *au, *raw;
2757         GtkWidget   *rev, *forw, *both;
2758         user_data_t *user_data;
2759         gint         channels, format;
2760
2761         g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2762
2763         /* Perhaps the user specified a directory instead of a file.
2764          * Check whether they did.
2765          */
2766         if (test_for_directory(g_dest) == EISDIR) {
2767                 /* It's a directory - set the file selection box to display it. */
2768                 set_last_open_dir(g_dest);
2769                 g_free(g_dest);
2770                 file_selection_set_current_folder((GtkWidget *)fc, get_last_open_dir());
2771                 gtk_file_chooser_set_current_name((GtkFileChooser *)fc, "");
2772                 return FALSE; /* run the dialog again */
2773         }
2774
2775 #if 0
2776         wav  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2777         sw   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2778 #endif
2779         au   = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2780         raw  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2781         rev  = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2782         forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2783         both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2784         user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2785
2786         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2787         * we don't support that codec. So we pop up a warning. Maybe it would be better to
2788         * disable the ok button or disable the buttons for direction if only one is not ok. The
2789         * problem is if we open the save voice dialog and then click the refresh button and maybe
2790         * the state changes, so we can't save anymore. In this case we should be able to update
2791         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2792         */
2793
2794         /* we can not save in both directions */
2795         if ((user_data->forward.saveinfo.saved == FALSE)
2796             && (user_data->reversed.saveinfo.saved == FALSE)
2797             && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2798                 /* there are many combinations here, we just exit when first matches */
2799                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2800                     || (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2801                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2802                         "Can't save in a file: Unsupported codec.");
2803                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2804                          || (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2805                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2806                         "Can't save in a file: Wrong length of captured packets.");
2807                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2808                          || (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2809                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2810                         "Can't save in a file: RTP data with padding.");
2811                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2812                          || (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2813                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2814                         "Can't save in a file: Not all data in all packets was captured.");
2815                 else
2816                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2817                         "Can't save in a file: File I/O problem.");
2818                 g_free(g_dest);
2819                 return TRUE; /* we're done */
2820         }
2821         /* we can not save forward direction */
2822         else if ((user_data->forward.saveinfo.saved == FALSE)
2823                  && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw)))
2824                      || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2825                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2826                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2827                         "Can't save forward direction in a file: Unsupported codec.");
2828                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2829                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2830                         "Can't save forward direction in a file: Wrong length of captured packets.");
2831                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2832                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2833                         "Can't save forward direction in a file: RTP data with padding.");
2834                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2835                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2836                         "Can't save forward direction in a file: Not all data in all packets was captured.");
2837                 else
2838                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2839                         "Can't save forward direction in a file: File I/O problem.");
2840                 g_free(g_dest);
2841                 return TRUE; /* we're done */
2842         }
2843         /* we can not save reversed direction */
2844         else if ((user_data->reversed.saveinfo.saved == FALSE)
2845                  && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2846                      || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2847                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2848                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2849                         "Can't save reversed direction in a file: Unsupported codec.");
2850                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2851                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2852                         "Can't save reversed direction in a file: Wrong length of captured packets.");
2853                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2854                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2855                         "Can't save reversed direction in a file: RTP data with padding.");
2856                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2857                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2858                         "Can't save reversed direction in a file: Not all data in all packets was captured.");
2859                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2860                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2861                         "Can't save reversed direction in a file: No RTP data.");
2862                 else
2863                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2864                         "Can't save reversed direction in a file: File I/O problem.");
2865                 g_free(g_dest);
2866                 return TRUE; /* we're done */
2867         }
2868
2869 #if 0
2870         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2871                 format = SAVE_WAV_FORMAT;
2872         else
2873 #endif
2874         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2875                 format = SAVE_AU_FORMAT;
2876 #if 0
2877         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2878                 format = SAVE_SW_FORMAT;
2879 #endif
2880         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2881                 format = SAVE_RAW_FORMAT;
2882         else
2883                 format = SAVE_NONE_FORMAT;
2884
2885         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2886                 channels = SAVE_REVERSE_DIRECTION_MASK;
2887         else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2888                 channels = SAVE_BOTH_DIRECTION_MASK;
2889         else
2890                 channels = SAVE_FORWARD_DIRECTION_MASK;
2891
2892         /* direction/format validity*/
2893         if (format == SAVE_AU_FORMAT)
2894         {
2895                 /* make sure streams are alaw/ulaw */
2896                 if ((channels & SAVE_FORWARD_DIRECTION_MASK)
2897                     && (user_data->forward.statinfo.pt != PT_PCMA)
2898                     && (user_data->forward.statinfo.pt != PT_PCMU)) {
2899                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2900                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2901                         g_free(g_dest);
2902                         return TRUE; /* we're done */
2903                 }
2904                 if ((channels & SAVE_REVERSE_DIRECTION_MASK)
2905                     && (user_data->reversed.statinfo.pt != PT_PCMA)
2906                     && (user_data->reversed.statinfo.pt != PT_PCMU)) {
2907                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2908                                 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2909                         g_free(g_dest);
2910                         return TRUE; /* we're done */
2911                 }
2912                 /* make sure pt's don't differ */
2913                 if ((channels == SAVE_BOTH_DIRECTION_MASK)
2914                     && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)) {
2915                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2916                                 "Can't save in a file: Forward and reverse direction differ in type");
2917                         g_free(g_dest);
2918                         return TRUE; /* we're done */
2919                 }
2920         }
2921         else if (format == SAVE_RAW_FORMAT)
2922         {
2923                 /* can't save raw in both directions */
2924                 if (channels == SAVE_BOTH_DIRECTION_MASK) {
2925                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2926                                 "Can't save in a file: Unable to save raw data in both directions");
2927                         g_free(g_dest);
2928                         return TRUE; /* we're done */
2929                 }
2930         }
2931         else
2932         {
2933                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2934                         "Can't save in a file: Invalid save format");
2935                 g_free(g_dest);
2936                 return TRUE; /* we're done */
2937         }
2938
2939         if (!copy_file(g_dest, channels, format, user_data)) {
2940                 /* XXX - report the error type! */
2941                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2942                         "An error occurred while saving voice in a file.");
2943                 g_free(g_dest);
2944                 return TRUE; /* we're done */
2945         }
2946
2947         g_free(g_dest);
2948         return TRUE; /* we're done */
2949 }
2950
2951 /****************************************************************************/
2952 /* when the user wants to save the voice information in a file */
2953 /* XXX support for different formats is currently commented out */
2954 static void
2955 on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2956 {
2957         GtkWidget *vertb;
2958         GtkWidget *grid1;
2959         GtkWidget *label_format;
2960         GtkWidget *channels_label;
2961         GtkWidget *forward_rb;
2962         GtkWidget *reversed_rb;
2963         GtkWidget *both_rb;
2964         /*GtkWidget *wav_rb;  GtkWidget *sw_rb;*/
2965         GtkWidget *au_rb;
2966         GtkWidget *raw_rb;
2967
2968         /* if we can't save in a file: wrong codec, cut packets or other errors */
2969         /* Should the error arise here or later when you click ok button ?
2970          * if we do it here, then we must disable the refresh button, so we don't do it here
2971          */
2972
2973 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2974         if (user_data->dlg.save_voice_as_w != NULL) {
2975                 /* There's already a Save voice info dialog box; reactivate it. */
2976                 reactivate_window(user_data->dlg.save_voice_as_w);
2977                 return;
2978         }
2979 #endif
2980         /* XXX - use file_selection from dlg_utils instead! */
2981         user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2982                                                                      GTK_WINDOW(user_data->dlg.notebook),
2983                                                                      GTK_FILE_CHOOSER_ACTION_SAVE,
2984                                                                      GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2985                                                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2986                                                                      NULL);
2987         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2988
2989         /* Container for each row of widgets */
2990         vertb =ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2991         gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2992         gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2993         gtk_widget_show (vertb);
2994
2995         grid1 = ws_gtk_grid_new ();
2996         gtk_widget_show (grid1);
2997         gtk_box_pack_start (GTK_BOX (vertb), grid1, FALSE, FALSE, 0);
2998         gtk_container_set_border_width (GTK_CONTAINER (grid1), 10);
2999         ws_gtk_grid_set_row_spacing (GTK_GRID (grid1), 20);
3000
3001 #if 0
3002         label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
3003         gtk_widget_show (label_format);
3004         ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 3, 1,
3005                                      (GtkAttachOptions) (GTK_FILL),
3006                                      (GtkAttachOptions) (0), 0, 0);
3007 #endif
3008
3009         label_format = gtk_label_new ("Format: ");
3010         gtk_widget_show (label_format);
3011         ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 1, 1,
3012                                      (GtkAttachOptions) (GTK_FILL),
3013                                      (GtkAttachOptions) (0), 0, 0);
3014
3015         gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
3016
3017         raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
3018         gtk_widget_show (raw_rb);
3019         ws_gtk_grid_attach_extended (GTK_GRID (grid1), raw_rb, 1, 0, 1, 1,
3020                                      (GtkAttachOptions) (GTK_FILL),
3021                                      (GtkAttachOptions) (0), 0, 0);
3022
3023
3024         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
3025         gtk_widget_show (au_rb);
3026         ws_gtk_grid_attach_extended (GTK_GRID (grid1), au_rb, 3, 0, 1, 1,
3027                                      (GtkAttachOptions) (GTK_FILL),
3028                                      (GtkAttachOptions) (0), 0, 0);
3029
3030 #if 0
3031         /* we support .au - ulaw*/
3032         wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
3033         gtk_widget_show (wav_rb);
3034         ws_gtk_grid_attach_extended (GTK_GRID (grid1), wav_rb, 1, 0, 1, 1,
3035                                      (GtkAttachOptions) (GTK_FILL),
3036                                      (GtkAttachOptions) (0), 0, 0);
3037
3038         sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit  ");
3039         gtk_widget_show (sw_rb);
3040         ws_gtk_grid_attach_extended (GTK_GRID (grid1), sw_rb, 2, 0, 1, 1,
3041                                      (GtkAttachOptions) (GTK_FILL),
3042                                      (GtkAttachOptions) (0), 0, 0);
3043         au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
3044         gtk_widget_show (au_rb);
3045         ws_gtk_grid_attach_extended (GTK_GRID (grid1), au_rb, 3, 0, 1, 1,
3046                                      (GtkAttachOptions) (GTK_FILL),
3047                                      (GtkAttachOptions) (0), 0, 0);
3048 #endif
3049
3050         channels_label = gtk_label_new ("Channels:    ");
3051         gtk_widget_show (channels_label);
3052         ws_gtk_grid_attach_extended (GTK_GRID (grid1), channels_label, 0, 1, 1, 1,
3053                                      (GtkAttachOptions) (GTK_FILL),
3054                                      (GtkAttachOptions) (0), 0, 0);
3055         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
3056
3057         forward_rb = gtk_radio_button_new_with_label (NULL, "forward    ");
3058         gtk_widget_show (forward_rb);
3059         ws_gtk_grid_attach_extended (GTK_GRID (grid1), forward_rb, 1, 1, 1, 1,
3060                                      (GtkAttachOptions) (GTK_FILL),
3061                                      (GtkAttachOptions) (0), 0, 0);
3062
3063         reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed    ");
3064         gtk_widget_show (reversed_rb);
3065         ws_gtk_grid_attach_extended (GTK_GRID (grid1), reversed_rb, 2, 1, 1, 1,
3066                                      (GtkAttachOptions) (GTK_FILL),
3067                                      (GtkAttachOptions) (0), 0, 0);
3068
3069         both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
3070         gtk_widget_show (both_rb);
3071         ws_gtk_grid_attach_extended (GTK_GRID (grid1), both_rb, 3, 1, 1, 1,
3072                                      (GtkAttachOptions) (GTK_FILL),
3073                                      (GtkAttachOptions) (0), 0, 0);
3074
3075
3076         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
3077
3078 #if 0
3079         /* if one direction is nok we don't allow saving
3080         XXX this is not ok since the user can click the refresh button and cause changes
3081         but we can not update this window. So we move all the decision on the time the ok
3082         button is clicked
3083         */
3084         if (user_data->forward.saved == FALSE) {
3085         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
3086         gtk_widget_set_sensitive(forward_rb, FALSE);
3087         gtk_widget_set_sensitive(both_rb, FALSE);
3088         }
3089         else if (user_data->reversed.saved == FALSE) {
3090         gtk_widget_set_sensitive(reversed_rb, FALSE);
3091         gtk_widget_set_sensitive(both_rb, FALSE);
3092         }
3093  #endif
3094
3095         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
3096         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
3097         /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
3098         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
3099         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
3100         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
3101         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
3102         g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
3103
3104         g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
3105                          G_CALLBACK(window_delete_event_cb), NULL);
3106         g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
3107                          G_CALLBACK(save_voice_as_destroy_cb), user_data);
3108
3109         gtk_widget_show(user_data->dlg.save_voice_as_w);
3110         window_present(user_data->dlg.save_voice_as_w);
3111
3112         /* "Run" the GtkFileChooserDialog.                                              */
3113         /* Upon exit: If "Accept" run the OK callback.                                  */
3114         /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
3115         /*            Destroy the window.                                               */
3116         /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
3117         /*      return with a TRUE status so that the dialog window will be destroyed.  */
3118         /*      Trying to re-run the dialog after popping up an alert box will not work */
3119         /*       since the user will not be able to dismiss the alert box.              */
3120         /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
3121         /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
3122         /*                                                                              */
3123         /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
3124         /*            GtkFileChooserDialog.                                             */
3125         while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
3126                 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
3127                         break;  /* we're done */
3128                 }
3129         }
3130         window_destroy(user_data->dlg.save_voice_as_w);
3131
3132 }
3133
3134
3135 /****************************************************************************/
3136 /* when we are finished with redisection, we add the label for the statistic */
3137 static void
3138 draw_stat(user_data_t *user_data)
3139 {
3140         gchar label_max[300];
3141         guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3142                 - user_data->forward.statinfo.start_seq_nr + 1;
3143         guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3144                 - user_data->reversed.statinfo.start_seq_nr + 1;
3145         guint32 f_total_nr = user_data->forward.statinfo.total_nr;
3146         guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
3147         gint32 f_lost = f_expected - f_total_nr;
3148         gint32 r_lost = r_expected - r_total_nr;
3149         double f_sumt = user_data->forward.statinfo.sumt;
3150         double f_sumTS = user_data->forward.statinfo.sumTS;
3151         double f_sumt2 = user_data->forward.statinfo.sumt2;
3152         double f_sumtTS = user_data->forward.statinfo.sumtTS;
3153
3154         double r_sumt = user_data->reversed.statinfo.sumt;
3155         double r_sumTS = user_data->reversed.statinfo.sumTS;
3156         double r_sumt2 = user_data->reversed.statinfo.sumt2;
3157         double r_sumtTS = user_data->reversed.statinfo.sumtTS;
3158         double f_perc, r_perc;
3159         double f_clock_drift = 1.0;
3160         double r_clock_drift = 1.0;
3161         double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
3162         double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
3163         guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
3164         guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
3165
3166         if (f_clock_rate == 0) {
3167                 f_clock_rate = 1;
3168         }
3169
3170         if (r_clock_rate == 0) {
3171                 r_clock_rate = 1;
3172         }
3173
3174         if (f_expected) {
3175                 f_perc = (double)(f_lost*100)/(double)f_expected;
3176         } else {
3177                 f_perc = 0;
3178         }
3179         if (r_expected) {
3180                 r_perc = (double)(r_lost*100)/(double)r_expected;
3181         } else {
3182                 r_perc = 0;
3183         }
3184
3185         if ((f_total_nr >0) && (f_sumt2 > 0)) {
3186                 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
3187         }
3188         if ((r_total_nr >0) && (r_sumt2 > 0)) {
3189                 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
3190         }
3191         g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3192                 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3193                 "Max skew = %.2f ms.\n"
3194                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
3195                 "   Sequence errors = %u \n"
3196                 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3197                 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3198                 user_data->forward.statinfo.max_jitter, user_data->forward.statinfo.mean_jitter,
3199                 user_data->forward.statinfo.max_skew,
3200                 f_expected, f_expected, f_lost, f_perc,
3201                 user_data->forward.statinfo.sequence,
3202                 f_duration / 1000,
3203                 f_duration * (f_clock_drift - 1.0),
3204                 f_clock_drift * f_clock_rate,
3205                 100.0 * (f_clock_drift - 1.0));
3206
3207         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3208         gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd), TRUE);
3209
3210         g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3211                 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3212                 "Max skew = %.2f ms.\n"
3213                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d (%.2f%%)"
3214                 "   Sequence errors = %u \n"
3215                 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3216                 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3217                 user_data->reversed.statinfo.max_jitter, user_data->reversed.statinfo.mean_jitter,
3218                 user_data->reversed.statinfo.max_skew,
3219                 r_expected, r_expected, r_lost, r_perc,
3220                 user_data->reversed.statinfo.sequence,
3221                 r_duration / 1000,
3222                 r_duration * (r_clock_drift - 1.0),
3223                 r_clock_drift * r_clock_rate,
3224                 100.0 * (r_clock_drift - 1.0));
3225
3226         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3227         gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev), TRUE);
3228
3229         return ;
3230 }
3231
3232
3233
3234 /****************************************************************************/
3235 /* append a line to list */
3236 static void
3237 add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
3238             double delta, double jitter, double skew, double bandwidth, gchar *status, gboolean marker,
3239             gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
3240 {
3241         GtkListStore *list_store;
3242
3243         if (strcmp(status, OK_TEXT) != 0) {
3244                 user_data->dlg.number_of_nok++;
3245         }
3246
3247         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
3248
3249         /* Creates a new row at position. iter will be changed to point to this new row.
3250          * If position is larger than the number of rows on the list, then the new row will be appended to the list.
3251          * The row will be filled with the values given to this function.
3252          * :
3253          * should generally be preferred when inserting rows in a sorted list store.
3254          */
3255         gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3256                              PACKET_COLUMN,        number,
3257                              SEQUENCE_COLUMN,      seq_num,
3258                              TIMESTAMP_COLUMN,     timestamp,
3259                              DELTA_COLUMN,         delta,
3260                              JITTER_COLUMN,        jitter,
3261                              SKEW_COLUMN,          skew,
3262                              IPBW_COLUMN,          bandwidth,
3263                              MARKER_COLUMN,        marker,
3264                              STATUS_COLUMN,        (char *)status,
3265                              DATE_COLUMN,          (char *)timeStr,
3266                              LENGTH_COLUMN,        pkt_len,
3267                              FOREGROUND_COLOR_COL, NULL,
3268                              BACKGROUND_COLOR_COL, (char *)color_str,
3269                              -1);
3270
3271         if (flags & STAT_FLAG_FIRST) {
3272                 /* Set first row as active */
3273                 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3274         }
3275 }
3276
3277 /****************************************************************************
3278 * Functions needed to present values from the list
3279 */
3280
3281
3282 /* Present boolean value */
3283 static void
3284 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3285                        GtkCellRenderer   *renderer,
3286                        GtkTreeModel      *model,
3287                        GtkTreeIter       *iter,
3288                        gpointer           user_data)
3289 {
3290         gboolean bool_val;
3291         gchar    buf[20];
3292         /* the col to get data from is in userdata */
3293         gint     bool_col = GPOINTER_TO_INT(user_data);
3294
3295         gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3296
3297         switch(bool_col) {
3298         case MARKER_COLUMN:
3299                 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3300                 break;
3301         default:
3302                 g_assert_not_reached();
3303                 break;
3304         }
3305         g_object_set(renderer, "text", buf, NULL);
3306 }
3307
3308 /* Create list */
3309 static GtkWidget *
3310 create_list(user_data_t* user_data)
3311 {
3312
3313         GtkListStore      *list_store;
3314         GtkWidget         *list;
3315         GtkTreeViewColumn *column;
3316         GtkCellRenderer   *renderer;
3317         GtkTreeSortable   *sortable;
3318         GtkTreeView       *list_view;
3319         GtkTreeSelection  *selection;
3320
3321         /* Create the store */
3322         list_store = gtk_list_store_new(N_COLUMN,       /* Total number of columns XXX  */
3323                                         G_TYPE_UINT,    /* Packet                       */
3324                                         G_TYPE_UINT,    /* Sequence                     */
3325                                         G_TYPE_UINT,    /* Time stamp                   */
3326                                         G_TYPE_FLOAT,   /* Delta(ms)                    */
3327                                         G_TYPE_FLOAT,   /* Filtered Jitter(ms)          */
3328                                         G_TYPE_FLOAT,   /* Skew(ms)                     */
3329                                         G_TYPE_FLOAT,   /* IP BW(kbps)                  */
3330                                         G_TYPE_BOOLEAN, /* Marker                       */
3331                                         G_TYPE_STRING,  /* Status                       */
3332                                         G_TYPE_STRING,  /* Date                         */
3333                                         G_TYPE_UINT,    /* Length                       */
3334                                         G_TYPE_STRING,  /* Foreground color             */
3335                                         G_TYPE_STRING); /* Background color             */
3336
3337         /* Create a view */
3338         list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3339
3340         list_view = GTK_TREE_VIEW(list);
3341         sortable = GTK_TREE_SORTABLE(list_store);
3342
3343         /* Speed up the list display */
3344         gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3345
3346         /* Setup the sortable columns */
3347         gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3348         gtk_tree_view_set_headers_clickable(list_view, FALSE);
3349
3350         /* The view now holds a reference.  We can get rid of our own reference */
3351         g_object_unref (G_OBJECT (list_store));
3352
3353         /*
3354          * Create the first column packet, associating the "text" attribute of the
3355          * cell_renderer to the first column of the model
3356          */
3357         renderer = gtk_cell_renderer_text_new ();
3358         column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3359                                                            "text",      PACKET_COLUMN,
3360                                                            "foreground", FOREGROUND_COLOR_COL,
3361                                                            "background", BACKGROUND_COLOR_COL,
3362                                                            NULL);
3363         gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3364         gtk_tree_view_column_set_resizable(column, TRUE);
3365         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3366         gtk_tree_view_column_set_min_width(column, 55);
3367
3368         /* Add the column to the view. */
3369         gtk_tree_view_append_column (list_view, column);
3370
3371         /* Sequence. */
3372         renderer = gtk_cell_renderer_text_new ();
3373         column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3374                                                            "text", SEQUENCE_COLUMN,
3375                                                            "foreground", FOREGROUND_COLOR_COL,
3376                                                            "background", BACKGROUND_COLOR_COL,
3377                                                            NULL);
3378         gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3379         gtk_tree_view_column_set_resizable(column, TRUE);
3380         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3381         gtk_tree_view_column_set_min_width(column, 75);
3382         gtk_tree_view_append_column (list_view, column);
3383
3384 #if 0
3385         Currently not visible
3386                 /* Time stamp. */
3387                 renderer = gtk_cell_renderer_text_new ();
3388         column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3389                                                            "text", TIMESTAMP_COLUMN,
3390                                                            "foreground", FOREGROUND_COLOR_COL,
3391                                                            "background", BACKGROUND_COLOR_COL,
3392                                                            NULL);
3393         gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3394         gtk_tree_view_column_set_resizable(column, TRUE);
3395         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3396         gtk_tree_view_column_set_min_width(column, 75);
3397         gtk_tree_view_append_column (list_view, column);
3398 #endif
3399         /* Delta(ms). */
3400         renderer = gtk_cell_renderer_text_new ();
3401         column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3402                                                            "text", DELTA_COLUMN,
3403                                                            "foreground", FOREGROUND_COLOR_COL,
3404                                                            "background", BACKGROUND_COLOR_COL,
3405                                                            NULL);
3406
3407         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3408                                                 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3409
3410         gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3411         gtk_tree_view_column_set_resizable(column, TRUE);
3412         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3413         gtk_tree_view_column_set_min_width(column, 75);
3414         gtk_tree_view_append_column (list_view, column);
3415
3416         /* Jitter(ms). */
3417         renderer = gtk_cell_renderer_text_new ();
3418         column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3419                                                            "text", JITTER_COLUMN,
3420                                                            "foreground", FOREGROUND_COLOR_COL,
3421                                                            "background", BACKGROUND_COLOR_COL,
3422                                                            NULL);
3423
3424         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3425                                                 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3426
3427         gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3428         gtk_tree_view_column_set_resizable(column, TRUE);
3429         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3430         gtk_tree_view_column_set_min_width(column, 110);
3431         gtk_tree_view_append_column (list_view, column);
3432
3433         /* Skew(ms). */
3434         renderer = gtk_cell_renderer_text_new ();
3435         column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3436                                                            "text", SKEW_COLUMN,
3437                                                            "foreground", FOREGROUND_COLOR_COL,
3438                                                            "background", BACKGROUND_COLOR_COL,
3439                                                            NULL);
3440
3441         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3442                                                 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3443
3444         gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3445         gtk_tree_view_column_set_resizable(column, TRUE);
3446         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3447         gtk_tree_view_column_set_min_width(column, 110);
3448         gtk_tree_view_append_column (list_view, column);
3449
3450         /* IP BW(kbps). */
3451         renderer = gtk_cell_renderer_text_new ();
3452         column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3453                                                            "text", IPBW_COLUMN,
3454                                                            "foreground", FOREGROUND_COLOR_COL,
3455                                                            "background", BACKGROUND_COLOR_COL,
3456                                                            NULL);
3457
3458         gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3459                                                 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3460
3461         gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3462         gtk_tree_view_column_set_resizable(column, TRUE);
3463         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3464         gtk_tree_view_column_set_min_width(column, 80);
3465         gtk_tree_view_append_column (list_view, column);
3466
3467         /* Marker. */
3468         renderer = gtk_cell_renderer_text_new ();
3469         column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3470                                                            "text", MARKER_COLUMN,
3471                                                            "foreground", FOREGROUND_COLOR_COL,
3472                                                            "background", BACKGROUND_COLOR_COL,
3473                                                            NULL);
3474
3475         gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3476                                                 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3477
3478         gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3479         gtk_tree_view_column_set_resizable(column, TRUE);
3480         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3481         gtk_tree_view_column_set_min_width(column, 60);
3482         gtk_tree_view_append_column (list_view, column);
3483
3484         /* Status. */
3485         renderer = gtk_cell_renderer_text_new ();
3486         column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3487                                                             "text", STATUS_COLUMN,
3488                                                             "foreground", FOREGROUND_COLOR_COL,
3489                                                             "background", BACKGROUND_COLOR_COL,
3490                                                             NULL);
3491         gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3492         gtk_tree_view_column_set_resizable(column, TRUE);
3493         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3494         gtk_tree_view_column_set_min_width(column, 100);
3495         gtk_tree_view_append_column (list_view, column);
3496
3497         /* Now enable the sorting of each column */
3498         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3499         gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3500
3501         /* Setup the selection handler */
3502         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3503         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3504
3505         g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3506                           G_CALLBACK (on_list_select_row),
3507                           user_data);
3508         return list;
3509 }
3510
3511 /****************************************************************************/
3512 /* Create the dialog box with all widgets */
3513 static void
3514 create_rtp_dialog(user_data_t* user_data)
3515 {
3516         GtkWidget *window;
3517         GtkWidget *list_fwd;
3518         GtkWidget *list_rev;
3519         GtkWidget *label_stats_fwd;
3520         GtkWidget *label_stats_rev;
3521         GtkWidget *notebook;
3522
3523         GtkWidget *main_vb, *page, *page_r;
3524         GtkWidget *label;
3525         GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3526         GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3527 #ifdef HAVE_LIBPORTAUDIO
3528         GtkWidget *player_bt;
3529 #endif /* HAVE_LIBPORTAUDIO */
3530         GtkWidget *graph_bt;
3531         gchar      label_forward[150];
3532         gchar      label_forward_tree[150];
3533         gchar      label_reverse[150];
3534
3535         gchar      str_src[16];
3536         gchar      str_dst[16];
3537
3538         window = dlg_window_new("Wireshark: RTP Stream Analysis");  /* transient_for top_level */
3539         gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3540
3541         /* Container for each row of widgets */
3542         main_vb =ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 2, FALSE);
3543         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3544         gtk_container_add(GTK_CONTAINER(window), main_vb);
3545         gtk_widget_show(main_vb);
3546
3547         /* Notebooks... */
3548         g_strlcpy(str_src, ep_address_to_display(&(user_data->src_fwd)), sizeof(str_src));
3549         g_strlcpy(str_dst, ep_address_to_display(&(user_data->dst_fwd)), sizeof(str_dst));
3550
3551         g_snprintf(label_forward, sizeof(label_forward),
3552                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X",
3553                 str_src, user_data->port_src_fwd, str_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3554
3555         g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3556                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X \n"
3557                 "Note many things affects the accurasy of the analysis, use with caution",
3558                 str_src, user_data->port_src_fwd, str_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3559
3560
3561         g_strlcpy(str_src, ep_address_to_display(&(user_data->src_rev)), sizeof(str_src));
3562         g_strlcpy(str_dst, ep_address_to_display(&(user_data->dst_rev)), sizeof(str_dst));
3563
3564         g_snprintf(label_reverse, sizeof(label_reverse),
3565                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = 0x%X \n"
3566                 "Note many things affects the accurasy of the analysis, use with caution",
3567                 str_src, user_data->port_src_rev, str_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3568
3569         /* Start a notebook for flipping between sets of changes */
3570         notebook = gtk_notebook_new();
3571         gtk_box_pack_start(GTK_BOX(main_vb), notebook, TRUE, TRUE, 0);
3572         g_object_set_data(G_OBJECT(window), "notebook", notebook);
3573
3574         user_data->dlg.notebook_signal_id =
3575                 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3576
3577         /* page for forward connection */
3578         page = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3579         gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3580
3581         /* direction label */
3582         label = gtk_label_new(label_forward);
3583         gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3584
3585         /* place for some statistics */
3586         label_stats_fwd = gtk_label_new("\n");
3587         gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3588
3589         /* scrolled window */
3590         scrolled_window = scrolled_window_new(NULL, NULL);
3591
3592         /* packet list */
3593         list_fwd = create_list(user_data);
3594         gtk_widget_show(list_fwd);
3595         gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3596         gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3597         gtk_widget_show(scrolled_window);
3598
3599         /* tab */
3600         label = gtk_label_new("  Forward Direction  ");
3601         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3602
3603         /* same page for reversed connection */
3604         page_r = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3605         gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3606         label = gtk_label_new(label_reverse);
3607         gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3608         label_stats_rev = gtk_label_new("\n");
3609         gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3610
3611         scrolled_window_r = scrolled_window_new(NULL, NULL);
3612
3613         list_rev = create_list(user_data);
3614         gtk_widget_show(list_rev);
3615         gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3616         gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3617         gtk_widget_show(scrolled_window_r);
3618
3619         label = gtk_label_new("  Reversed Direction  ");
3620         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3621
3622         /* page for help&about or future */
3623 #if 0
3624         page_help = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
3625         label = gtk_label_new("     Future    ");
3626         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3627         frame = gtk_frame_new("");
3628         text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution, ...");
3629         gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3630         gtk_container_add(GTK_CONTAINER(frame), text);
3631         gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3632         gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3633 #endif
3634
3635         /* show all notebooks */
3636         gtk_widget_show_all(notebook);
3637
3638         /* buttons */
3639         box4 = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3640         gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3641         gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3642         gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3643         gtk_box_set_spacing(GTK_BOX (box4), 0);
3644         gtk_widget_show(box4);
3645
3646         voice_bt = gtk_button_new_with_label("Save payload...");
3647         gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3648         gtk_widget_show(voice_bt);
3649         g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3650
3651         csv_bt = gtk_button_new_with_label("Save as CSV...");
3652         gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3653         gtk_widget_show(csv_bt);
3654         g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3655
3656         refresh_bt = ws_gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3657         gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3658         gtk_widget_show(refresh_bt);
3659         g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3660
3661         goto_bt = ws_gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3662         gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3663         gtk_widget_show(goto_bt);
3664         g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3665
3666         graph_bt = gtk_button_new_with_label("Graph");
3667         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3668         gtk_widget_show(graph_bt);
3669         g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3670
3671 #ifdef HAVE_LIBPORTAUDIO
3672         player_bt = ws_gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
3673         gtk_container_add(GTK_CONTAINER(box4), player_bt);
3674         gtk_widget_show(player_bt);
3675         g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
3676         /*gtk_widget_set_tooltip_text (player_bt, "Launch the RTP player to listen the audio stream");*/
3677 #endif /* HAVE_LIBPORTAUDIO */
3678
3679         next_bt = gtk_button_new_with_label("Next non-Ok");
3680         gtk_container_add(GTK_CONTAINER(box4), next_bt);
3681         gtk_widget_show(next_bt);
3682         g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3683
3684         close_bt = ws_gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3685         gtk_container_add(GTK_CONTAINER(box4), close_bt);
3686         gtk_widget_set_can_default(close_bt, TRUE);
3687         gtk_widget_show(close_bt);
3688         window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3689
3690         g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3691         g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3692
3693         gtk_widget_show(window);
3694         window_present(window);
3695
3696
3697         /* some widget references need to be saved for outside use */
3698         user_data->dlg.window = window;
3699         user_data->dlg.list_fwd = list_fwd;
3700         user_data->dlg.list_rev = list_rev;
3701         user_data->dlg.label_stats_fwd = label_stats_fwd;
3702         user_data->dlg.label_stats_rev = label_stats_rev;
3703         user_data->dlg.notebook = notebook;
3704         user_data->dlg.selected_list = list_fwd;
3705         user_data->dlg.number_of_nok = 0;
3706
3707         /*
3708          * select the initial row
3709          */
3710         gtk_widget_grab_focus(list_fwd);
3711
3712 }
3713
3714
3715 /****************************************************************************/
3716 static gboolean
3717 process_node(proto_node *ptree_node, header_field_info *hfinformation,
3718                       const gchar* proto_field, guint32* p_result)
3719 {
3720         field_info        *finfo;
3721         proto_node        *proto_sibling_node;
3722         header_field_info *hfssrc;
3723         ipv4_addr         *ipv4;
3724
3725         finfo = PNODE_FINFO(ptree_node);
3726
3727         /* Caller passed top of the protocol tree. Expected child node */
3728         g_assert(finfo);
3729
3730         if (hfinformation == (finfo->hfinfo)) {
3731                 hfssrc = proto_registrar_get_byname(proto_field);
3732                 if (hfssrc == NULL)
3733                         return FALSE;
3734                 for (ptree_node = ptree_node->first_child;
3735                      ptree_node != NULL;
3736                      ptree_node = ptree_node->next) {
3737                         finfo = PNODE_FINFO(ptree_node);
3738                         if (hfssrc == finfo->hfinfo) {
3739                                 if (hfinformation->type == FT_IPv4) {
3740                                         ipv4 = (ipv4_addr *)fvalue_get(&finfo->value);
3741                                         *p_result = ipv4_get_net_order_addr(ipv4);
3742                                 }
3743                                 else {
3744                                         *p_result = fvalue_get_uinteger(&finfo->value);
3745                                 }
3746                                 return TRUE;
3747                         }
3748                 }
3749                 if (!ptree_node)
3750                         return FALSE;
3751         }
3752
3753         proto_sibling_node = ptree_node->next;
3754
3755         if (proto_sibling_node) {
3756                 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3757         }
3758         else
3759         return FALSE;
3760 }
3761
3762 /****************************************************************************/
3763 static gboolean
3764 get_int_value_from_proto_tree(proto_tree  *protocol_tree,
3765                               const gchar *proto_name,
3766                               const gchar *proto_field,
3767                               guint32     *p_result)
3768 {
3769         proto_node        *ptree_node;
3770         header_field_info *hfinformation;
3771
3772         hfinformation = proto_registrar_get_byname(proto_name);
3773         if (hfinformation == NULL)
3774                 return FALSE;
3775
3776         ptree_node = ((proto_node *)protocol_tree)->first_child;
3777         if (!ptree_node)
3778                 return FALSE;
3779
3780         return process_node(ptree_node, hfinformation, proto_field, p_result);
3781 }
3782
3783
3784 /****************************************************************************/
3785 void
3786 rtp_analysis(address *src_fwd,
3787              guint32  port_src_fwd,
3788              address *dst_fwd,
3789              guint32  port_dst_fwd,
3790              guint32  ssrc_fwd,
3791              address *src_rev,
3792              guint32  port_src_rev,
3793              address *dst_rev,
3794              guint32  port_dst_rev,
3795              guint32  ssrc_rev)
3796 {
3797         user_data_t *user_data;
3798         int fd;
3799         int i;
3800         static GdkColor col[MAX_GRAPHS] = {
3801                 {0,     0x0000, 0x0000, 0x0000}, /* Black */
3802                 {0,     0xffff, 0x0000, 0x0000}, /* Red */
3803                 {0,     0x0000, 0xffff, 0x0000}, /* Green */
3804                 {0,             0xdddd, 0xcccc, 0x6666}, /* Light amber yellow */
3805                 {0,             0x6666, 0xcccc, 0xdddd}, /* Light bluish cyan */
3806                 {0,     0x0000, 0x0000, 0xffff}  /* Blue */
3807         };
3808         static GdkRGBA rgba_col[MAX_GRAPHS] = {
3809                 {0.0,   0.0,   0.0,   1.0}, /* Black */
3810                 {1.0,   0.0,   0.1,   1.0}, /* Red */
3811                 {0.0,   1.0,   0.0,   1.0}, /* Green */
3812                 {0.867, 0.800, 0.400, 1.0}, /* Light amber yellow */
3813                 {0.400, 0.800, 0.867, 1.0}, /* Light bluish cyan */
3814                 {0.0,   0.0,   1.0,   1.0}, /* Blue */
3815         };
3816
3817         char *tempname;
3818
3819         /* init */
3820         user_data = (user_data_t *)g_malloc(sizeof(user_data_t));
3821
3822         COPY_ADDRESS(&(user_data->src_fwd), src_fwd);
3823         user_data->port_src_fwd = port_src_fwd;
3824         COPY_ADDRESS(&(user_data->dst_fwd), dst_fwd);
3825         user_data->port_dst_fwd = port_dst_fwd;
3826         user_data->ssrc_fwd = ssrc_fwd;
3827         COPY_ADDRESS(&(user_data->src_rev), src_rev);
3828         user_data->port_src_rev = port_src_rev;
3829         COPY_ADDRESS(&(user_data->dst_rev), dst_rev);
3830         user_data->port_dst_rev = port_dst_rev;
3831         user_data->ssrc_rev = ssrc_rev;
3832
3833
3834         /* file names for storing sound data */
3835         fd = create_tempfile(&tempname, "wireshark_rtp_f");
3836         if (fd < 0) {
3837                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3838                               "Can't create temporary file for RTP analysis:\n%s.",
3839                               g_strerror(errno));
3840                 g_free(user_data);
3841                 return;
3842         }
3843         user_data->f_tempname = g_strdup(tempname);
3844         ws_close(fd);
3845         fd = create_tempfile(&tempname, "wireshark_rtp_r");
3846         if (fd < 0) {
3847                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3848                               "Can't create temporary file for RTP analysis:\n%s.",
3849                               g_strerror(errno));
3850                 g_free(user_data->f_tempname);
3851                 g_free(user_data);
3852                 return;
3853         }
3854         user_data->r_tempname = g_strdup(tempname);
3855         ws_close(fd);
3856         user_data->forward.saveinfo.fp     = NULL;
3857         user_data->reversed.saveinfo.fp    = NULL;
3858         user_data->dlg.save_voice_as_w     = NULL;
3859         user_data->dlg.save_csv_as_w       = NULL;
3860         user_data->dlg.dialog_graph.window = NULL;
3861
3862         /* init dialog_graph */
3863         user_data->dlg.dialog_graph.needs_redraw = TRUE;
3864         user_data->dlg.dialog_graph.interval     = tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
3865         user_data->dlg.dialog_graph.draw_area    = NULL;
3866 #if GTK_CHECK_VERSION(2,22,0)
3867         user_data->dlg.dialog_graph.surface      = NULL;
3868 #else
3869         user_data->dlg.dialog_graph.pixmap       = NULL;
3870 #endif
3871         user_data->dlg.dialog_graph.scrollbar            = NULL;
3872         user_data->dlg.dialog_graph.scrollbar_adjustment = NULL;
3873         user_data->dlg.dialog_graph.surface_width        = 500;
3874         user_data->dlg.dialog_graph.surface_height       = 200;
3875         user_data->dlg.dialog_graph.pixels_per_tick      = pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3876         user_data->dlg.dialog_graph.max_y_units          = AUTO_MAX_YSCALE;
3877         user_data->dlg.dialog_graph.last_interval        = 0xffffffff;
3878         user_data->dlg.dialog_graph.max_interval         = 0;
3879         user_data->dlg.dialog_graph.num_items            = 0;
3880         user_data->dlg.dialog_graph.start_time           = -1;
3881
3882         for (i = 0; i < MAX_GRAPHS; i++) {
3883                 user_data->dlg.dialog_graph.graph[i].color.pixel      = 0;
3884                 user_data->dlg.dialog_graph.graph[i].color.red        = col[i].red;
3885                 user_data->dlg.dialog_graph.graph[i].color.green      = col[i].green;
3886                 user_data->dlg.dialog_graph.graph[i].color.blue       = col[i].blue;
3887                 user_data->dlg.dialog_graph.graph[i].rgba_color.red   = rgba_col[i].red;
3888                 user_data->dlg.dialog_graph.graph[i].rgba_color.green = rgba_col[i].green;
3889                 user_data->dlg.dialog_graph.graph[i].rgba_color.blue  = rgba_col[i].blue;
3890                 user_data->dlg.dialog_graph.graph[i].rgba_color.alpha = rgba_col[i].alpha;
3891                 user_data->dlg.dialog_graph.graph[i].display          = TRUE;
3892                 user_data->dlg.dialog_graph.graph[i].display_button   = NULL;
3893                 user_data->dlg.dialog_graph.graph[i].ud               = user_data;
3894         }
3895
3896         /* create the dialog box */
3897         create_rtp_dialog(user_data);
3898
3899         /* proceed as if the Refresh button would have been pressed */
3900         on_refresh_bt_clicked(NULL, user_data);
3901 }
3902
3903 /****************************************************************************/
3904 /* entry point from main menu */
3905 void
3906 rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3907 {
3908         address       src_fwd;
3909         guint32       port_src_fwd;
3910         address       dst_fwd;
3911         guint32       port_dst_fwd;
3912         guint32       ssrc_fwd      = 0;
3913         address       src_rev;
3914         guint32       port_src_rev;
3915         address       dst_rev;
3916         guint32       port_dst_rev;
3917         guint32       ssrc_rev      = 0;
3918         unsigned int  version_fwd;
3919
3920         gchar         filter_text[256];
3921         dfilter_t    *sfcode;
3922         capture_file *cf;
3923         frame_data   *fdata;
3924         GList        *strinfo_list;
3925         GList        *filtered_list = NULL;
3926         guint         nfound;
3927         epan_dissect_t     edt;
3928         rtp_stream_info_t *strinfo;
3929
3930         /* Try to compile the filter. */
3931         g_strlcpy(filter_text, "rtp && rtp.version && rtp.ssrc && (ip || ipv6)", sizeof(filter_text));
3932         if (!dfilter_compile(filter_text, &sfcode)) {
3933                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3934                 return;
3935         }
3936         /* we load the current file into cf variable */
3937         cf = &cfile;
3938         fdata = cf->current_frame;
3939
3940         /* we are on the selected frame now */
3941         if (fdata == NULL)
3942                 return; /* if we exit here it's an error */
3943
3944         /* dissect the current record */
3945         if (!cf_read_record(cf, fdata))
3946                 return; /* error reading the record */
3947         epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
3948         epan_dissect_prime_dfilter(&edt, sfcode);
3949         epan_dissect_run(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
3950
3951         /* if it is not an rtp packet, show the rtpstream dialog */
3952         if (!dfilter_apply_edt(sfcode, &edt)) {
3953                 epan_dissect_cleanup(&edt);
3954                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3955                     "Please select an RTP packet.");
3956                 return;
3957         }
3958
3959         /* ok, it is a RTP frame, so let's get the ip and port values */
3960         COPY_ADDRESS(&(src_fwd), &(edt.pi.src));
3961         COPY_ADDRESS(&(dst_fwd), &(edt.pi.dst));
3962         port_src_fwd = edt.pi.srcport;
3963         port_dst_fwd = edt.pi.destport;
3964
3965         /* assume the inverse ip/port combination for the reverse direction */
3966         COPY_ADDRESS(&(src_rev), &(edt.pi.dst));
3967         COPY_ADDRESS(&(dst_rev), &(edt.pi.src));
3968         port_src_rev = edt.pi.destport;
3969         port_dst_rev = edt.pi.srcport;
3970
3971         /* check if it is RTP Version 2 */
3972         if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3973                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3974                               "Only RTP version 2 is supported.");
3975                 return;
3976         }
3977
3978         /* now we need the SSRC value of the current frame */
3979         if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3980                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3981                     "SSRC value not found.");
3982                 return;
3983         }
3984
3985         /* Scan for rtpstream */
3986         rtpstream_scan();
3987         /* search for reversed direction in the global rtp streams list */
3988         nfound = 0;
3989         strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3990         while (strinfo_list)
3991         {
3992                 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3993                 if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_fwd))
3994                     && (strinfo->src_port == port_src_fwd)
3995                     && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_fwd)))
3996                     && (strinfo->dest_port == port_dst_fwd))
3997                 {
3998                         filtered_list = g_list_prepend(filtered_list, strinfo);
3999                 }
4000
4001                 if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_rev))
4002                     && (strinfo->src_port == port_src_rev)
4003                     && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_rev)))
4004                     && (strinfo->dest_port == port_dst_rev))
4005                 {
4006                         ++nfound;
4007                         filtered_list = g_list_append(filtered_list, strinfo);
4008                         if (ssrc_rev == 0)
4009                                 ssrc_rev = strinfo->ssrc;
4010                 }
4011
4012                 strinfo_list = g_list_next(strinfo_list);
4013         }
4014
4015         /* if more than one reverse streams found, we let the user choose the right one */
4016         if (nfound>1) {
4017                 rtpstream_dlg_show(filtered_list);
4018                 return;
4019         }
4020         else {
4021                 rtp_analysis(
4022                         &src_fwd,
4023                         port_src_fwd,
4024                         &dst_fwd,
4025                         port_dst_fwd,
4026                         ssrc_fwd,
4027                         &src_rev,
4028                         port_src_rev,
4029                         &dst_rev,
4030                         port_dst_rev,
4031                         ssrc_rev
4032                         );
4033         }
4034 }
4035
4036 /****************************************************************************/
4037 static void
4038 rtp_analysis_init(const char *dummy _U_, void *userdata _U_)
4039 {
4040         rtp_analysis_cb(NULL, NULL);
4041 }
4042
4043 /****************************************************************************/
4044 static stat_tap_ui rtp_analysis_ui = {
4045         REGISTER_STAT_GROUP_GENERIC,
4046         NULL,
4047         "rtp",
4048         rtp_analysis_init,
4049         -1,
4050         0,
4051         NULL
4052 };
4053
4054 void
4055 register_tap_listener_rtp_analysis(void)
4056 {
4057         register_stat_tap_ui(&rtp_analysis_ui, NULL);
4058 }