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