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