2 * IAX2 analysis addition for Wireshark
6 * based on rtp_analysis.c
7 * Copyright 2003, Alcatel Business Systems
8 * By Lars Ruoff <lars.ruoff@gmx.net>
11 * Copyright 2003, Iskratel, Ltd, Kranj
12 * By Miha Jemec <m.jemec@iskratel.si>
14 * Graph. Copyright 2004, Verso Technology
15 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
16 * Based on io_stat.c by Ronnie Sahlberg
18 * Wireshark - Network traffic analyzer
19 * By Gerald Combs <gerald@wireshark.org>
20 * Copyright 1998 Gerald Combs
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.
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.
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.
56 #include <epan/epan_dissect.h>
57 #include <epan/filesystem.h>
58 #include <epan/pint.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>
69 #include "../alert_box.h"
70 #include "../simple_dialog.h"
71 #include "../stat_menu.h"
72 #include "../progress_dlg.h"
73 #include "../tempfile.h"
74 #include <wsutil/file_util.h>
76 #include "gtk/gtkglobals.h"
77 #include "gtk/dlg_utils.h"
78 #include "gtk/file_dlg.h"
79 #include "gtk/gui_utils.h"
80 #include "gtk/gui_stat_menu.h"
82 #include "gtk/rtp_analysis.h"
83 #include "gtk/iax2_analysis.h"
84 #include "gtk/rtp_stream.h"
85 #include "gtk/rtp_stream_dlg.h"
86 #include "gtk/utf8_entities.h"
88 #include "gtk/old-gtk-compat.h"
100 BACKGROUND_COLOR_COL,
101 N_COLUMN /* The number of columns */
104 /****************************************************************************/
106 #define NUM_GRAPH_ITEMS 100000
107 #define MAX_YSCALE 16
108 #define AUTO_MAX_YSCALE_INDEX 0
109 #define AUTO_MAX_YSCALE 0
111 #define GRAPH_FWD_JITTER 0
112 #define GRAPH_FWD_DIFF 1
113 #define GRAPH_REV_JITTER 2
114 #define GRAPH_REV_DIFF 3
115 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
117 #define MAX_PIXELS_PER_TICK 4
118 #define DEFAULT_PIXELS_PER_TICK_INDEX 1
119 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
120 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
122 #define MAX_TICK_VALUES 5
123 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
124 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
125 typedef struct _dialog_graph_graph_item_t {
128 } dialog_graph_graph_item_t;
130 typedef struct _dialog_graph_graph_t {
131 struct _user_data_t *ud;
132 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
135 GtkWidget *display_button;
139 } dialog_graph_graph_t;
142 typedef struct _dialog_graph_t {
143 gboolean needs_redraw;
144 gint32 interval_index; /* index into tick_interval_values array */
145 gint32 interval; /* measurement interval in ms */
146 guint32 last_interval;
147 guint32 max_interval; /* XXX max_interval and num_items are redundant */
149 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
151 GtkWidget *draw_area;
152 #if GTK_CHECK_VERSION(2,22,0)
153 cairo_surface_t *surface;
157 GtkAdjustment *scrollbar_adjustment;
158 GtkWidget *scrollbar;
161 int pixels_per_tick_index; /* index into pixels_per_tick array */
163 int max_y_units_index; /* index into yscale_max array */
168 typedef struct _dialog_data_t {
173 GtkWidget *label_stats_fwd;
174 GtkWidget *label_stats_rev;
175 GtkWidget *selected_list;
177 GtkTreeSelection *selected_list_sel;
178 gint selected_list_row;
180 GtkWidget *save_voice_as_w;
181 GtkWidget *save_csv_as_w;
182 gint notebook_signal_id;
183 dialog_graph_t dialog_graph;
186 #define OK_TEXT "[ Ok ]"
188 /* type of error when saving voice in a file didn't succeed */
191 TAP_RTP_WRONG_LENGTH,
192 TAP_RTP_PADDING_ERROR,
194 TAP_RTP_FILE_OPEN_ERROR,
195 TAP_RTP_FILE_WRITE_ERROR,
199 typedef struct _tap_iax2_save_info_t {
202 error_type_t error_type;
204 } tap_iax2_save_info_t;
207 /* structure that holds the information about the forward and reversed direction */
208 struct _info_direction {
209 tap_iax2_stat_t statinfo;
210 tap_iax2_save_info_t saveinfo;
213 #define SILENCE_PCMU (guint8)0xFF
214 #define SILENCE_PCMA (guint8)0x55
216 /* structure that holds general information about the connection
217 * and structures for both directions */
218 typedef struct _user_data_t {
219 /* tap associated data*/
221 guint16 port_src_fwd;
223 guint16 port_dst_fwd;
225 guint16 port_src_rev;
227 guint16 port_dst_rev;
229 struct _info_direction forward;
230 struct _info_direction reversed;
235 /* dialog associated data */
242 static const gchar *titles[7] = {
252 #define SAVE_FORWARD_DIRECTION_MASK 0x01
253 #define SAVE_REVERSE_DIRECTION_MASK 0x02
254 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
256 #define SAVE_NONE_FORMAT 0
257 #define SAVE_WAV_FORMAT 1
258 #define SAVE_AU_FORMAT 2
259 #define SAVE_SW_FORMAT 3
260 #define SAVE_RAW_FORMAT 4
263 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data);
264 /****************************************************************************/
265 static void enable_graph(dialog_graph_graph_t *dgg)
272 static void dialog_graph_reset(user_data_t* user_data);
276 /****************************************************************************/
279 /****************************************************************************/
280 /* when there is a [re]reading of packet's */
282 iax2_reset(void *user_data_arg)
284 user_data_t *user_data = user_data_arg;
285 user_data->forward.statinfo.first_packet = TRUE;
286 user_data->reversed.statinfo.first_packet = TRUE;
287 user_data->forward.statinfo.max_delta = 0;
288 user_data->reversed.statinfo.max_delta = 0;
289 user_data->forward.statinfo.max_jitter = 0;
290 user_data->reversed.statinfo.max_jitter = 0;
291 user_data->forward.statinfo.mean_jitter = 0;
292 user_data->reversed.statinfo.mean_jitter = 0;
293 user_data->forward.statinfo.delta = 0;
294 user_data->reversed.statinfo.delta = 0;
295 user_data->forward.statinfo.diff = 0;
296 user_data->reversed.statinfo.diff = 0;
297 user_data->forward.statinfo.jitter = 0;
298 user_data->reversed.statinfo.jitter = 0;
299 user_data->forward.statinfo.bandwidth = 0;
300 user_data->reversed.statinfo.bandwidth = 0;
301 user_data->forward.statinfo.total_bytes = 0;
302 user_data->reversed.statinfo.total_bytes = 0;
303 user_data->forward.statinfo.bw_start_index = 0;
304 user_data->reversed.statinfo.bw_start_index = 0;
305 user_data->forward.statinfo.bw_index = 0;
306 user_data->reversed.statinfo.bw_index = 0;
307 user_data->forward.statinfo.timestamp = 0;
308 user_data->reversed.statinfo.timestamp = 0;
309 user_data->forward.statinfo.max_nr = 0;
310 user_data->reversed.statinfo.max_nr = 0;
311 user_data->forward.statinfo.total_nr = 0;
312 user_data->reversed.statinfo.total_nr = 0;
313 user_data->forward.statinfo.sequence = 0;
314 user_data->reversed.statinfo.sequence = 0;
315 user_data->forward.statinfo.start_seq_nr = 0;
316 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
317 user_data->forward.statinfo.stop_seq_nr = 0;
318 user_data->reversed.statinfo.stop_seq_nr = 0;
319 user_data->forward.statinfo.cycles = 0;
320 user_data->reversed.statinfo.cycles = 0;
321 user_data->forward.statinfo.under = FALSE;
322 user_data->reversed.statinfo.under = FALSE;
323 user_data->forward.statinfo.start_time = 0;
324 user_data->reversed.statinfo.start_time = 0;
325 user_data->forward.statinfo.time = 0;
326 user_data->reversed.statinfo.time = 0;
327 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
328 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
330 user_data->forward.saveinfo.count = 0;
331 user_data->reversed.saveinfo.count = 0;
332 user_data->forward.saveinfo.saved = FALSE;
333 user_data->reversed.saveinfo.saved = FALSE;
335 /* clear the dialog box lists */
336 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
337 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
339 /* reset graph info */
340 dialog_graph_reset(user_data);
343 /* XXX check for error at fclose? */
344 if (user_data->forward.saveinfo.fp != NULL)
345 fclose(user_data->forward.saveinfo.fp);
346 if (user_data->reversed.saveinfo.fp != NULL)
347 fclose(user_data->reversed.saveinfo.fp);
348 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
349 if (user_data->forward.saveinfo.fp == NULL)
350 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
351 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
352 if (user_data->reversed.saveinfo.fp == NULL)
353 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
357 /****************************************************************************/
358 static gboolean iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
360 dialog_graph_graph_item_t *it;
364 /* we sometimes get called when dgg is disabled.
365 this is a bug since the tap listener should be removed first */
370 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
373 * Find which interval this is supposed to go in and store the
374 * interval index as idx
376 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
377 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
379 rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
383 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
385 /* some sanity checks */
386 if(idx>=NUM_GRAPH_ITEMS){
390 /* update num_items */
391 if(idx > dgg->ud->dlg.dialog_graph.num_items){
392 dgg->ud->dlg.dialog_graph.num_items=idx;
393 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
397 * Find the appropriate dialog_graph_graph_item_t structure
402 * Use the max value to highlight RTP problems
404 if (value > it->value) {
407 it->flags = it->flags | statinfo->flags;
412 /****************************************************************************/
413 /* here we can redraw the output */
415 static void iax2_draw(void *prs _U_)
420 /* forward declarations */
421 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
422 double delta, double jitter, double bandwidth, gchar *status,
423 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
425 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
426 tap_iax2_stat_t *statinfo, packet_info *pinfo,
427 const struct _iax2_info_t *iax2info);
429 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
430 tap_iax2_stat_t *statinfo,
432 const struct _iax2_info_t *iax2info);
435 /****************************************************************************/
436 /* whenever a IAX2 packet is seen by the tap listener */
437 static gboolean iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
439 user_data_t *user_data = user_data_arg;
440 const struct _iax2_info_t *iax2info = iax2info_arg;
442 /* we ignore packets that are not displayed */
443 if (pinfo->fd->flags.passed_dfilter == 0)
446 /* we ignore packets that carry no data */
447 if (iax2info->payload_len == 0)
450 /* is it the forward direction? */
451 else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
452 && user_data->port_src_fwd == pinfo->srcport
453 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
454 && user_data->port_dst_fwd == pinfo->destport) {
455 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
456 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
457 &(user_data->forward.statinfo),
459 (guint32)(user_data->forward.statinfo.jitter*1000000));
460 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
461 &(user_data->forward.statinfo),
463 (guint32)(user_data->forward.statinfo.diff*1000000));
464 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
465 &(user_data->forward.statinfo), pinfo, iax2info);
466 iax2_packet_save_payload(&(user_data->forward.saveinfo),
467 &(user_data->forward.statinfo), pinfo, iax2info);
469 /* is it the reversed direction? */
470 else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
471 && user_data->port_src_rev == pinfo->srcport
472 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
473 && user_data->port_dst_rev == pinfo->destport) {
474 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
475 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
476 &(user_data->reversed.statinfo),
478 (guint32)(user_data->reversed.statinfo.jitter*1000000));
479 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
480 &(user_data->reversed.statinfo),
482 (guint32)(user_data->reversed.statinfo.diff*1000000));
483 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
484 &(user_data->reversed.statinfo), pinfo, iax2info);
485 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
486 &(user_data->reversed.statinfo), pinfo, iax2info);
492 /****************************************************************************/
493 /* This comes from tap-rtp-common.c */
494 /****************************************************************************/
496 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
498 const struct _iax2_info_t *iax2info)
501 double current_jitter;
505 /* check payload type */
506 if (iax2info->ftype == AST_FRAME_VOICE) {
507 if (iax2info->csub != statinfo->pt)
508 statinfo->flags |= STAT_FLAG_PT_CHANGE;
509 statinfo->pt = iax2info->csub;
512 /* store the current time and calculate the current jitter */
513 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
514 current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
515 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
516 statinfo->delta = current_time-(statinfo->time);
517 statinfo->jitter = current_jitter;
518 statinfo->diff = current_diff;
520 /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
521 statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
522 statinfo->bw_history[statinfo->bw_index].time = current_time;
523 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
524 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
525 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
526 statinfo->bw_start_index++;
527 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
529 statinfo->total_bytes += iax2info->payload_len + 24;
530 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
531 statinfo->bw_index++;
532 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
535 /* is this the first packet we got in this direction? */
536 if (statinfo->first_packet) {
537 statinfo->start_seq_nr = 0;
538 statinfo->start_time = current_time;
540 statinfo->jitter = 0;
542 statinfo->flags |= STAT_FLAG_FIRST;
543 statinfo->first_packet = FALSE;
545 /* is it a regular packet? */
546 if (!(statinfo->flags & STAT_FLAG_FIRST)
547 && !(statinfo->flags & STAT_FLAG_MARKER)
548 && !(statinfo->flags & STAT_FLAG_PT_CN)
549 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
550 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
551 /* include it in maximum delta calculation */
552 if (statinfo->delta > statinfo->max_delta) {
553 statinfo->max_delta = statinfo->delta;
554 statinfo->max_nr = pinfo->fd->num;
556 /* maximum and mean jitter calculation */
557 if (statinfo->jitter > statinfo->max_jitter) {
558 statinfo->max_jitter = statinfo->jitter;
560 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
562 /* regular payload change? (CN ignored) */
563 if (!(statinfo->flags & STAT_FLAG_FIRST)
564 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
565 if ((statinfo->pt != statinfo->reg_pt)
566 && (statinfo->reg_pt != PT_UNDEFINED)) {
567 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
571 /* set regular payload*/
572 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
573 statinfo->reg_pt = statinfo->pt;
576 /* TODO: lost packets / duplicated: we should infer this from timestamp... */
577 statinfo->time = current_time;
578 statinfo->timestamp = iax2info->timestamp; /* millisecs */
579 statinfo->stop_seq_nr = 0;
580 statinfo->total_nr++;
587 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
588 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
589 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
590 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
591 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
594 /****************************************************************************/
595 /* adds statistics information from the packet to the list */
596 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
597 tap_iax2_stat_t *statinfo, packet_info *pinfo,
598 const struct _iax2_info_t *iax2info _U_)
605 /* GdkColor color = COLOR_DEFAULT; */
607 then = pinfo->fd->abs_ts.secs;
608 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
609 tm_tmp = localtime(&then);
610 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
613 tm_tmp->tm_year + 1900,
619 /* Default to using black on white text if nothing below overrides it */
620 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
622 if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
623 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
624 /* color = COLOR_ERROR; */
625 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
627 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
628 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
629 /* color = COLOR_WARNING; */
630 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
632 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
633 g_snprintf(status,sizeof(status),"Incorrect timestamp");
634 /* color = COLOR_WARNING; */
635 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
637 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
638 && !(statinfo->flags & STAT_FLAG_FIRST)
639 && !(statinfo->flags & STAT_FLAG_PT_CN)
640 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
641 && !(statinfo->flags & STAT_FLAG_MARKER)) {
642 g_snprintf(status,sizeof(status),"Marker missing?");
643 /* color = COLOR_WARNING; */
644 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
647 if (statinfo->flags & STAT_FLAG_MARKER) {
648 /* color = COLOR_WARNING; */
649 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
651 g_snprintf(status,sizeof(status),OK_TEXT);
653 /* is this the first packet we got in this direction? */
654 if (statinfo->flags & STAT_FLAG_FIRST) {
655 add_to_list(list, user_data,
661 timeStr, pinfo->fd->pkt_len,
666 add_to_list(list, user_data,
668 statinfo->delta*1000,
669 statinfo->jitter*1000,
672 timeStr, pinfo->fd->pkt_len,
679 #define MAX_SILENCE_TICKS 1000000
680 /****************************************************************************/
681 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
682 tap_iax2_stat_t *statinfo,
684 const struct _iax2_info_t *iax2info)
689 /* is this the first packet we got in this direction? */
690 if (statinfo->flags & STAT_FLAG_FIRST) {
691 if (saveinfo->fp == NULL) {
692 saveinfo->saved = FALSE;
693 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
696 saveinfo->saved = TRUE;
699 /* save the voice information */
700 /* if there was already an error, we quit */
701 if (saveinfo->saved == FALSE)
704 /* if the captured length and packet length aren't equal, we quit */
705 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
706 saveinfo->saved = FALSE;
707 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
711 if (iax2info->payload_len > 0) {
712 data = iax2info->payload_data;
713 nchars = fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
714 if (nchars != iax2info->payload_len) {
715 /* Write error or short write */
716 saveinfo->saved = FALSE;
717 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
720 saveinfo->count+=iax2info->payload_len;
722 if (fflush(saveinfo->fp) == EOF) {
724 saveinfo->saved = FALSE;
725 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
728 saveinfo->saved = TRUE;
736 /****************************************************************************/
739 /****************************************************************************/
741 /****************************************************************************/
742 /* close the dialog window and remove the tap listener */
743 static void on_iax2_window_destroy(GtkWidget *win _U_, gpointer data)
745 user_data_t *user_data = data;
747 /* remove tap listener */
748 protect_thread_critical_region();
749 remove_tap_listener(user_data);
750 unprotect_thread_critical_region();
752 /* close and remove temporary files */
753 if (user_data->forward.saveinfo.fp != NULL)
754 fclose(user_data->forward.saveinfo.fp);
755 if (user_data->reversed.saveinfo.fp != NULL)
756 fclose(user_data->reversed.saveinfo.fp);
757 /*XXX: test for error **/
758 ws_remove(user_data->f_tempname);
759 ws_remove(user_data->r_tempname);
761 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
762 /* destroy save_csv_as window if open */
763 if (user_data->dlg.save_csv_as_w != NULL)
764 window_destroy(user_data->dlg.save_csv_as_w);
766 /* destroy save_voice_as window if open */
767 if (user_data->dlg.save_voice_as_w != NULL)
768 window_destroy(user_data->dlg.save_voice_as_w);
770 /* destroy graph window if open */
771 if (user_data->dlg.dialog_graph.window != NULL)
772 window_destroy(user_data->dlg.dialog_graph.window);
775 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
776 /* XXX: Is this still true for GTK2 ??? */
777 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
779 g_free(user_data->f_tempname);
780 g_free(user_data->r_tempname);
785 /****************************************************************************/
786 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
791 user_data_t *user_data = data;
793 user_data->dlg.selected_list =
794 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
796 user_data->dlg.selected_list_row = 0;
799 /****************************************************************************/
800 static void on_list_select_row(GtkTreeSelection *selection,
803 user_data_t *user_data = data;
805 user_data->dlg.selected_list_sel = selection;
809 /****************************************************************************/
810 static void dialog_graph_set_title(user_data_t* user_data)
814 if (!user_data->dlg.dialog_graph.window){
817 title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
818 get_addr_name(&(user_data->ip_src_fwd)),
819 user_data->port_src_fwd,
820 get_addr_name(&(user_data->ip_dst_fwd)),
821 user_data->port_dst_fwd,
822 get_addr_name(&(user_data->ip_src_rev)),
823 user_data->port_src_rev,
824 get_addr_name(&(user_data->ip_dst_rev)),
825 user_data->port_dst_rev);
827 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
833 /****************************************************************************/
834 static void dialog_graph_reset(user_data_t* user_data)
838 user_data->dlg.dialog_graph.needs_redraw=TRUE;
839 for(i=0;i<MAX_GRAPHS;i++){
840 for(j=0;j<NUM_GRAPH_ITEMS;j++){
841 dialog_graph_graph_item_t *dggi;
842 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
847 user_data->dlg.dialog_graph.last_interval=0xffffffff;
848 user_data->dlg.dialog_graph.max_interval=0;
849 user_data->dlg.dialog_graph.num_items=0;
851 /* create the color titles near the filter buttons */
852 for(i=0;i<MAX_GRAPHS;i++){
855 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
856 sizeof (user_data->dlg.dialog_graph.graph[0].title),
857 "%s: %s:%u to %s:%u",
859 get_addr_name(&(user_data->ip_src_fwd)),
860 user_data->port_src_fwd,
861 get_addr_name(&(user_data->ip_dst_fwd)),
862 user_data->port_dst_fwd);
865 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
866 sizeof(user_data->dlg.dialog_graph.graph[0].title),
867 "%s: %s:%u to %s:%u",
869 get_addr_name(&(user_data->ip_src_rev)),
870 user_data->port_src_rev,
871 get_addr_name(&(user_data->ip_dst_rev)),
872 user_data->port_dst_rev);
876 dialog_graph_set_title(user_data);
879 /****************************************************************************/
880 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
882 dialog_graph_graph_item_t *it;
889 /****************************************************************************/
890 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
893 g_snprintf(buf, buf_len, "%ds",t/1000000);
894 } else if(t>=1000000){
895 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
897 g_snprintf(buf, buf_len, "%dms",t/1000);
899 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
901 g_snprintf(buf, buf_len, "%dus",t);
905 /****************************************************************************/
906 static void dialog_graph_draw(user_data_t* user_data)
909 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
910 gint32 current_interval;
911 guint32 left_x_border;
912 guint32 right_x_border;
913 guint32 top_y_border;
914 guint32 bottom_y_border;
916 int label_width, label_height;
917 int label_width_mid, label_height_mid;
918 guint32 draw_width, draw_height;
919 char label_string[15];
920 GtkAllocation widget_alloc;
924 guint32 num_time_intervals;
925 guint32 max_value; /* max value of seen data */
926 guint32 max_y; /* max value of the Y scale */
928 if(!user_data->dlg.dialog_graph.needs_redraw){
931 user_data->dlg.dialog_graph.needs_redraw=FALSE;
934 * Find the length of the intervals we have data for
935 * so we know how large arrays we need to malloc()
937 num_time_intervals=user_data->dlg.dialog_graph.num_items;
938 /* if there isnt anything to do, just return */
939 if(num_time_intervals==0){
942 num_time_intervals+=1;
943 /* XXX move this check to _packet() */
944 if(num_time_intervals>NUM_GRAPH_ITEMS){
945 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
950 * find the max value so we can autoscale the y axis
953 for(i=0;i<MAX_GRAPHS;i++){
956 if(!user_data->dlg.dialog_graph.graph[i].display){
959 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
962 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
964 /* keep track of the max value we have encountered */
974 #if GTK_CHECK_VERSION(2,22,0)
975 cr = cairo_create (user_data->dlg.dialog_graph.surface);
977 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
979 cairo_set_source_rgb (cr, 1, 1, 1);
980 gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
985 widget_alloc.height);
991 * Calculate the y scale we should use
993 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
994 max_y=yscale_max[MAX_YSCALE-1];
995 for(i=MAX_YSCALE-1;i>0;i--){
996 if(max_value<yscale_max[i]){
1001 /* the user had specified an explicit y scale to use */
1002 max_y=user_data->dlg.dialog_graph.max_y_units;
1006 * Calculate size of borders surrounding the plot
1007 * The border on the right side needs to be adjusted depending
1008 * on the width of the text labels.
1010 print_time_scale_string(label_string, sizeof(label_string), max_y);
1011 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1012 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1013 print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1014 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1015 pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1016 if (label_width_mid > label_width) {
1017 label_width = label_width_mid;
1018 label_height = label_height_mid;
1022 right_x_border=label_width+20;
1024 bottom_y_border=label_height+20;
1028 * Calculate the size of the drawing area for the actual plot
1030 draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
1031 draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
1035 * Draw the y axis and labels
1036 * (we always draw the y scale with 11 ticks along the axis)
1038 #if GTK_CHECK_VERSION(2,22,0)
1039 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1041 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1043 cairo_set_line_width (cr, 1.0);
1044 cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
1045 cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+0.5);
1054 /* first, middle and last tick are slightly longer */
1058 #if GTK_CHECK_VERSION(2,22,0)
1059 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1061 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1063 cairo_set_line_width (cr, 1.0);
1065 user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,
1066 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1069 user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
1070 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1073 /* draw the labels */
1075 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1076 pango_layout_set_text(layout, label_string, -1);
1077 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1078 #if GTK_CHECK_VERSION(2,22,0)
1079 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1081 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1084 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1085 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1086 pango_cairo_show_layout (cr, layout);
1091 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1092 pango_layout_set_text(layout, label_string, -1);
1093 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1094 #if GTK_CHECK_VERSION(2,22,0)
1095 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1097 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1100 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1101 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1102 pango_cairo_show_layout (cr, layout);
1107 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1108 pango_layout_set_text(layout, label_string, -1);
1109 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1110 #if GTK_CHECK_VERSION(2,22,0)
1111 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1113 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1116 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1117 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1118 pango_cairo_show_layout (cr, layout);
1127 * if we have not specified the last_interval via the gui,
1128 * then just pick the current end of the capture so that is scrolls
1129 * nicely when doing live captures
1131 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1132 last_interval=user_data->dlg.dialog_graph.max_interval;
1134 last_interval=user_data->dlg.dialog_graph.last_interval;
1141 /* plot the x-scale */
1142 #if GTK_CHECK_VERSION(2,22,0)
1143 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1145 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1147 cairo_set_line_width (cr, 1.0);
1148 cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1149 cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1153 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1154 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1155 first_interval*=user_data->dlg.dialog_graph.interval;
1162 while(interval_delta<((last_interval-first_interval)/10)){
1163 interval_delta*=delta_multiplier;
1164 if(delta_multiplier==5){
1171 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1174 /* if pixels_per_tick is <5, only draw every 10 ticks */
1175 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1179 if(current_interval%interval_delta){
1185 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1186 #if GTK_CHECK_VERSION(2,22,0)
1187 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1189 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1191 cairo_set_line_width (cr, 1.0);
1192 cairo_move_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1193 cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
1198 if(user_data->dlg.dialog_graph.interval>=1000){
1199 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1200 } else if(user_data->dlg.dialog_graph.interval>=100){
1201 g_snprintf(label_string, sizeof(label_string),
1202 "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1203 } else if(user_data->dlg.dialog_graph.interval>=10){
1204 g_snprintf(label_string, sizeof(label_string),
1205 "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1207 g_snprintf(label_string, sizeof(label_string),
1208 "%d.%3ds", current_interval/1000,current_interval%1000);
1210 pango_layout_set_text(layout, label_string, -1);
1211 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1212 #if GTK_CHECK_VERSION(2,22,0)
1213 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1215 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1218 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1219 user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
1220 pango_cairo_show_layout (cr, layout);
1233 * Draw "x" for Sequence Errors and "m" for Marks
1235 /* Draw the labels Fwd and Rev */
1236 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
1237 pango_layout_set_text(layout, label_string, -1);
1238 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1239 #if GTK_CHECK_VERSION(2,22,0)
1240 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1242 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1245 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1246 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
1247 pango_cairo_show_layout (cr, layout);
1251 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
1252 pango_layout_set_text(layout, label_string, -1);
1253 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1254 #if GTK_CHECK_VERSION(2,22,0)
1255 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1257 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1260 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1261 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
1262 pango_cairo_show_layout (cr, layout);
1266 /* Draw the marks */
1267 for(i=MAX_GRAPHS-1;i>=0;i--){
1269 guint32 x_pos/*, prev_x_pos*/;
1271 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1272 if (!user_data->dlg.dialog_graph.graph[i].display){
1275 /* initialize prev x/y to the low left corner of the graph */
1276 /* 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; */
1278 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1279 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;
1281 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1282 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1283 g_strlcpy(label_string, "x", sizeof(label_string));
1285 g_strlcpy(label_string, "m", sizeof(label_string));
1288 pango_layout_set_text(layout, label_string, -1);
1289 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1290 #if GTK_CHECK_VERSION(2,22,0)
1291 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1293 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1297 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
1298 pango_cairo_show_layout (cr, layout);
1303 /* prev_x_pos=x_pos; */
1307 g_object_unref(G_OBJECT(layout));
1310 * Loop over all graphs and draw them
1312 for(i=MAX_GRAPHS-1;i>=0;i--){
1314 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1315 if (!user_data->dlg.dialog_graph.graph[i].display){
1318 /* initialize prev x/y to the low left corner of the graph */
1319 /* 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; */
1320 prev_y_pos=draw_height-1+top_y_border;
1322 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1324 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;
1325 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1329 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1332 /* dont need to draw anything if the segment
1333 * is entirely above the top of the graph
1335 if( (prev_y_pos==0) && (y_pos==0) ){
1337 /* prev_x_pos=x_pos; */
1342 #if GTK_CHECK_VERSION(2,22,0)
1343 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1345 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1347 gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
1348 cairo_set_line_width (cr, 1.0);
1349 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1350 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1356 /* prev_x_pos=x_pos; */
1361 cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1363 #if GTK_CHECK_VERSION(2,22,0)
1364 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1366 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1368 cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1374 /* update the scrollbar */
1375 gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
1376 gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1377 gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1378 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1379 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1381 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1383 gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment, last_interval - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1384 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1385 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1389 /****************************************************************************/
1390 static void dialog_graph_redraw(user_data_t* user_data)
1392 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1393 dialog_graph_draw(user_data);
1396 /****************************************************************************/
1398 static void draw_area_destroy_cb(GtkWidget *widget _U_, gpointer data)
1400 user_data_t *user_data = data;
1402 user_data->dlg.dialog_graph.window = NULL;
1405 /****************************************************************************/
1406 static gboolean draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1408 user_data_t *user_data = data;
1409 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1411 #if GTK_CHECK_VERSION(2,22,0)
1412 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1414 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1416 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1424 /****************************************************************************/
1425 static gboolean draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1427 user_data_t *user_data = data;
1428 GtkAllocation widget_alloc;
1431 #if GTK_CHECK_VERSION(2,22,0)
1432 if(user_data->dlg.dialog_graph.surface){
1433 g_object_unref(user_data->dlg.dialog_graph.surface);
1434 user_data->dlg.dialog_graph.surface=NULL;
1436 gtk_widget_get_allocation(widget, &widget_alloc);
1437 user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1438 CAIRO_CONTENT_COLOR,
1440 widget_alloc.height);
1442 if(user_data->dlg.dialog_graph.pixmap){
1443 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1444 user_data->dlg.dialog_graph.pixmap=NULL;
1447 gtk_widget_get_allocation(widget, &widget_alloc);
1448 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1450 widget_alloc.height,
1453 user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
1454 user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
1456 #if GTK_CHECK_VERSION(2,22,0)
1457 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1459 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1461 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1462 cairo_set_source_rgb (cr, 1, 1, 1);
1466 dialog_graph_redraw(user_data);
1470 /****************************************************************************/
1471 static void scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1473 user_data_t *user_data = data;
1476 mi=(guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment) + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1477 if(user_data->dlg.dialog_graph.last_interval==mi){
1480 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1481 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1485 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1487 dialog_graph_redraw(user_data);
1491 /****************************************************************************/
1492 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1494 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1495 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), user_data);
1497 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1499 /* signals needed to handle backing pixmap */
1500 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(draw_area_expose_event), user_data);
1501 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(draw_area_configure_event), user_data);
1503 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1504 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1506 /* create the associated scrollbar */
1507 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1508 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1509 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1510 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1511 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1514 /****************************************************************************/
1515 static void disable_graph(dialog_graph_graph_t *dgg)
1519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1524 /****************************************************************************/
1525 static void filter_box_display_button_cb(GtkWidget *widget _U_, gpointer data)
1527 dialog_graph_graph_t *dgg = data;
1529 /* this graph is not active, just update display and redraw */
1530 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1532 dialog_graph_redraw(dgg->ud);
1537 cf_retap_packets(&cfile);
1538 dialog_graph_redraw(dgg->ud);
1543 /****************************************************************************/
1544 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1550 hbox=gtk_hbox_new(FALSE, 3);
1551 gtk_container_add(GTK_CONTAINER(box), hbox);
1552 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1553 gtk_widget_show(hbox);
1555 g_snprintf(str, sizeof(str), "Graph %d", num);
1556 dgg->display_button=gtk_toggle_button_new_with_label(str);
1557 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1558 gtk_widget_show(dgg->display_button);
1559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1560 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_box_display_button_cb), dgg);
1562 label=gtk_label_new(dgg->title);
1563 gtk_widget_show(label);
1564 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1566 #if GTK_CHECK_VERSION(3,0,0)
1568 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1569 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1570 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1571 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1572 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1577 /****************************************************************************/
1578 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1585 frame=gtk_frame_new("Graphs");
1586 gtk_container_add(GTK_CONTAINER(box), frame);
1587 gtk_widget_show(frame);
1589 vbox=gtk_vbox_new(FALSE, 1);
1590 gtk_container_add(GTK_CONTAINER(frame), vbox);
1591 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1592 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1593 gtk_widget_show(vbox);
1595 for(i=0;i<MAX_GRAPHS;i++){
1596 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1599 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1600 gtk_widget_show(label);
1601 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1606 /****************************************************************************/
1607 static void yscale_select(GtkWidget *item, gpointer data)
1609 user_data_t *user_data = data;
1612 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1614 user_data->dlg.dialog_graph.max_y_units_index=i;
1615 user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1616 dialog_graph_redraw(user_data);
1619 /****************************************************************************/
1620 static void pixels_per_tick_select(GtkWidget *item, gpointer data)
1622 user_data_t *user_data = data;
1625 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1627 user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1628 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1629 dialog_graph_redraw(user_data);
1632 /****************************************************************************/
1633 static void tick_interval_select(GtkWidget *item, gpointer data)
1635 user_data_t *user_data = data;
1638 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1640 user_data->dlg.dialog_graph.interval_index=i;
1641 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1642 cf_retap_packets(&cfile);
1643 dialog_graph_redraw(user_data);
1646 /****************************************************************************/
1648 create_yscale_max_menu_items(user_data_t* user_data)
1651 GtkWidget *combo_box;
1654 combo_box = gtk_combo_box_text_new();
1656 for(i=0;i<MAX_YSCALE;i++){
1657 if(yscale_max[i]==AUTO_MAX_YSCALE){
1658 g_strlcpy(str,"Auto",sizeof(str));
1659 } else if (yscale_max[i] < 1000000) {
1660 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1662 g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1664 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1666 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1667 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), user_data);
1672 /****************************************************************************/
1674 create_pixels_per_tick_menu_items(user_data_t *user_data)
1677 GtkWidget *combo_box;
1680 combo_box = gtk_combo_box_text_new();
1682 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1683 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1684 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1686 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1688 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), user_data);
1693 /****************************************************************************/
1695 create_tick_interval_menu_items(user_data_t *user_data)
1697 GtkWidget *combo_box;
1701 combo_box = gtk_combo_box_text_new();
1703 for(i=0;i<MAX_TICK_VALUES;i++){
1704 if(tick_interval_values[i]>=1000){
1705 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1706 } else if(tick_interval_values[i]>=100){
1707 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1708 } else if(tick_interval_values[i]>=10){
1709 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1711 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1713 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1715 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1716 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), user_data);
1721 /****************************************************************************/
1722 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1726 GtkWidget *combo_box;
1728 hbox=gtk_hbox_new(FALSE, 0);
1729 gtk_container_add(GTK_CONTAINER(box), hbox);
1730 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1731 gtk_widget_show(hbox);
1733 label=gtk_label_new(name);
1734 gtk_widget_show(label);
1735 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1737 combo_box = (*func)(user_data);
1738 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1739 gtk_widget_show(combo_box);
1742 /****************************************************************************/
1743 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1745 GtkWidget *frame_vbox;
1749 frame_vbox=gtk_vbox_new(FALSE, 0);
1750 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1751 gtk_widget_show(frame_vbox);
1753 frame = gtk_frame_new("X Axis");
1754 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1755 gtk_widget_show(frame);
1757 vbox=gtk_vbox_new(FALSE, 0);
1758 gtk_container_add(GTK_CONTAINER(frame), vbox);
1759 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1760 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1761 gtk_widget_show(vbox);
1763 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1764 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1766 frame = gtk_frame_new("Y Axis");
1767 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1768 gtk_widget_show(frame);
1770 vbox=gtk_vbox_new(FALSE, 0);
1771 gtk_container_add(GTK_CONTAINER(frame), vbox);
1772 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1773 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1774 gtk_widget_show(vbox);
1776 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1781 /****************************************************************************/
1782 static void dialog_graph_init_window(user_data_t* user_data)
1786 GtkWidget *bt_close;
1788 /* create the main window */
1789 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1791 vbox=gtk_vbox_new(FALSE, 0);
1792 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1793 gtk_widget_show(vbox);
1795 create_draw_area(user_data, vbox);
1797 hbox=gtk_hbox_new(FALSE, 3);
1798 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1799 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1800 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1801 gtk_widget_show(hbox);
1803 create_filter_area(user_data, hbox);
1804 create_ctrl_area(user_data, hbox);
1806 dialog_graph_set_title(user_data);
1808 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1809 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1810 gtk_widget_show(hbox);
1812 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1813 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1815 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1817 gtk_widget_show(user_data->dlg.dialog_graph.window);
1818 window_present(user_data->dlg.dialog_graph.window);
1823 /****************************************************************************/
1824 static void on_graph_bt_clicked(GtkWidget *bt _U_, gpointer data)
1826 user_data_t *user_data = data;
1828 if (user_data->dlg.dialog_graph.window != NULL) {
1829 /* There's already a graph window; reactivate it. */
1830 reactivate_window(user_data->dlg.dialog_graph.window);
1834 dialog_graph_init_window(user_data);
1838 /****************************************************************************/
1839 static void on_goto_bt_clicked(GtkWidget *bt _U_, gpointer data)
1841 user_data_t *user_data = data;
1843 GtkTreeModel *model;
1844 GtkTreeSelection *selection;
1847 selection = user_data->dlg.selected_list_sel;
1849 if (selection==NULL)
1852 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1853 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1854 cf_goto_frame(&cfile, fnumber);
1859 static void draw_stat(user_data_t *user_data);
1861 /****************************************************************************/
1862 /* re-dissects all packets */
1863 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data)
1865 user_data_t *user_data = data;
1866 GString *error_string;
1868 /* remove tap listener */
1869 protect_thread_critical_region();
1870 remove_tap_listener(user_data);
1871 unprotect_thread_critical_region();
1873 /* register tap listener */
1874 error_string = register_tap_listener("IAX2", user_data, NULL, 0,
1875 iax2_reset, iax2_packet, iax2_draw);
1876 if (error_string != NULL) {
1877 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1878 g_string_free(error_string, TRUE);
1882 /* retap all packets */
1883 cf_retap_packets(&cfile);
1885 /* draw statistics info */
1886 draw_stat(user_data);
1890 /****************************************************************************/
1891 static void on_next_bt_clicked(GtkWidget *bt _U_, gpointer data)
1893 user_data_t *user_data = data;
1895 GtkTreeModel *model;
1897 GtkTreeSelection *selection;
1900 selection = user_data->dlg.selected_list_sel;
1902 if (selection==NULL)
1906 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1907 while (gtk_tree_model_iter_next (model,&iter)) {
1908 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1909 if (strcmp(text, OK_TEXT) != 0) {
1910 gtk_tree_selection_select_iter (selection, &iter);
1911 path = gtk_tree_model_get_path(model, &iter);
1912 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1915 gtk_tree_path_free(path);
1922 if (user_data->dlg.number_of_nok>1){
1923 /* Get the first iter and select it before starting over */
1924 gtk_tree_model_get_iter_first(model, &iter);
1925 gtk_tree_selection_select_iter (selection, &iter);
1931 /****************************************************************************/
1932 /* when we want to save the information */
1933 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1936 GtkWidget *rev, *forw, *both;
1937 user_data_t *user_data;
1939 GtkListStore *store;
1941 GtkTreeModel *model;
1942 gboolean more_items = TRUE;
1944 /* To Hold data from the list row */
1945 guint packet; /* Packet */
1946 gfloat delta; /* Delta(ms) */
1947 gfloat jitter; /* Jitter(ms) */
1948 gfloat ipbw; /* IP BW(kbps) */
1949 char *status_str; /* Status */
1950 char *date_str; /* Date */
1951 guint length; /* Length */
1956 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1958 /* Perhaps the user specified a directory instead of a file.
1959 * Check whether they did.
1961 if (test_for_directory(g_dest) == EISDIR) {
1962 /* It's a directory - set the file selection box to display it. */
1963 set_last_open_dir(g_dest);
1964 file_selection_set_current_folder(fc, get_last_open_dir());
1965 gtk_file_chooser_set_current_name(fc, "");
1967 return FALSE; /* run the dialog again */
1969 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1970 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1971 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1972 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1974 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
1975 fp = ws_fopen(g_dest, "w");
1977 open_failure_alert_box(g_dest, errno, TRUE);
1979 return TRUE; /* we're done */
1982 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
1983 fprintf(fp, "Forward\n");
1985 write_failure_alert_box(g_dest, errno);
1988 return TRUE; /* we're done */
1992 for(j = 0; j < NUM_COLS; j++) {
1994 fprintf(fp,"\"%s\"",titles[j]);
1996 fprintf(fp,",\"%s\"",titles[j]);
2001 write_failure_alert_box(g_dest, errno);
2004 return TRUE; /* we're done */
2006 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2007 store = GTK_LIST_STORE(model);
2008 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2010 while (more_items) {
2011 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2020 fprintf(fp, "\"%u\"",packet);
2021 fprintf(fp, ",\"%.2f\"", delta);
2022 fprintf(fp, ",\"%.2f\"", jitter);
2023 fprintf(fp, ",\"%.2f\"", ipbw);
2024 fprintf(fp, ",\"%s\"", status_str);
2025 fprintf(fp, ",\"%s\"", date_str);
2026 fprintf(fp, ",\"%u\"", length);
2031 write_failure_alert_box(g_dest, errno);
2034 return TRUE; /* we're done */
2037 more_items = gtk_tree_model_iter_next (model,&iter);
2041 if (fclose(fp) == EOF) {
2042 write_failure_alert_box(g_dest, errno);
2044 return TRUE; /* we're done */
2048 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2050 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2051 fp = ws_fopen(g_dest, "a");
2053 open_failure_alert_box(g_dest, errno, TRUE);
2055 return TRUE; /* we're done */
2057 fprintf(fp, "\nReverse\n");
2059 write_failure_alert_box(g_dest, errno);
2062 return TRUE; /* we're done */
2065 fp = ws_fopen(g_dest, "w");
2067 open_failure_alert_box(g_dest, errno, TRUE);
2069 return TRUE; /* we're done */
2072 for(j = 0; j < NUM_COLS; j++) {
2074 fprintf(fp,"\"%s\"",titles[j]);
2076 fprintf(fp,",\"%s\"",titles[j]);
2081 write_failure_alert_box(g_dest, errno);
2084 return TRUE; /* we're done */
2086 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2087 store = GTK_LIST_STORE(model);
2088 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2092 while (more_items) {
2093 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2102 fprintf(fp, "\"%u\"",packet);
2103 fprintf(fp, ",\"%.2f\"", delta);
2104 fprintf(fp, ",\"%.2f\"", jitter);
2105 fprintf(fp, ",\"%.2f\"", ipbw);
2106 fprintf(fp, ",\"%s\"", status_str);
2107 fprintf(fp, ",\"%s\"", date_str);
2108 fprintf(fp, ",\"%u\"", length);
2113 write_failure_alert_box(g_dest, errno);
2116 return TRUE; /* we're done */
2119 more_items = gtk_tree_model_iter_next (model,&iter);
2122 if (fclose(fp) == EOF) {
2123 write_failure_alert_box(g_dest, errno);
2125 return TRUE; /* we're done */
2129 return TRUE; /* we're done */
2132 static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2134 user_data_t *user_data = data;
2136 user_data->dlg.save_csv_as_w = NULL;
2139 /* when the user wants to save the csv information in a file */
2140 static void save_csv_as_cb(GtkWidget *bt _U_, gpointer data)
2142 user_data_t *user_data = data;
2145 GtkWidget *label_format;
2146 GtkWidget *channels_label;
2147 GtkWidget *forward_rb;
2148 GtkWidget *reversed_rb;
2151 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2152 if (user_data->dlg.save_csv_as_w != NULL) {
2153 /* There's already a Save CSV info dialog box; reactivate it. */
2154 reactivate_window(user_data->dlg.save_csv_as_w);
2158 user_data->dlg.save_csv_as_w =
2159 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2160 GTK_WINDOW(user_data->dlg.notebook),
2161 GTK_FILE_CHOOSER_ACTION_SAVE,
2162 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2163 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2165 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2166 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2168 /* Build our "extra widget" to be added to the file chooser widget */
2169 /* Container for each row of widgets */
2170 vertb = gtk_vbox_new(FALSE, 0);
2171 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2172 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2173 gtk_widget_show (vertb);
2175 table1 = gtk_table_new (2, 4, FALSE);
2176 gtk_widget_show (table1);
2177 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2178 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2179 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2181 label_format = gtk_label_new ("Format: Comma Separated Values");
2182 gtk_widget_show (label_format);
2183 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2184 (GtkAttachOptions) (GTK_FILL),
2185 (GtkAttachOptions) (0), 0, 0);
2186 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2188 channels_label = gtk_label_new ("Channels: ");
2189 gtk_widget_show (channels_label);
2190 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2191 (GtkAttachOptions) (GTK_FILL),
2192 (GtkAttachOptions) (0), 0, 0);
2193 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2195 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2196 gtk_widget_show (forward_rb);
2197 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2198 (GtkAttachOptions) (GTK_FILL),
2199 (GtkAttachOptions) (0), 0, 0);
2201 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2202 gtk_widget_show (reversed_rb);
2203 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2204 (GtkAttachOptions) (GTK_FILL),
2205 (GtkAttachOptions) (0), 0, 0);
2207 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2208 gtk_widget_show (both_rb);
2209 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2210 (GtkAttachOptions) (GTK_FILL),
2211 (GtkAttachOptions) (0), 0, 0);
2213 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2215 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2216 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2217 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2218 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2220 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2221 G_CALLBACK(window_delete_event_cb), NULL);
2222 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2223 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2225 gtk_widget_show(user_data->dlg.save_csv_as_w);
2226 window_present(user_data->dlg.save_csv_as_w);
2228 /* "Run" the GtkFileChooserDialog. */
2229 /* Upon exit: If "Accept" run the OK callback. */
2230 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2231 /* Destroy the window. */
2232 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2233 /* return with a TRUE status so that the dialog window will be destroyed. */
2234 /* Trying to re-run the dialog after popping up an alert box will not work */
2235 /* since the user will not be able to dismiss the alert box. */
2236 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2237 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2239 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2240 /* GtkFileChooserDialog. */
2241 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2242 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2243 break; /* we're done */
2246 window_destroy(user_data->dlg.save_csv_as_w);
2249 /****************************************************************************/
2250 static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2252 user_data_t *user_data = data;
2254 /* Note that we no longer have a Save voice info dialog box. */
2255 user_data->dlg.save_voice_as_w = NULL;
2258 /****************************************************************************/
2259 /* here we save it into a file that user specified */
2260 /* XXX what about endians here? could go something wrong? */
2261 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2263 int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
2264 gchar f_pd[1] = {0};
2265 gchar r_pd[1] = {0};
2268 guint32 f_write_silence = 0;
2269 guint32 r_write_silence = 0;
2271 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2272 gboolean stop_flag = FALSE;
2275 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2278 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2284 /* open file for saving */
2285 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2292 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2294 if (format == SAVE_AU_FORMAT) /* au format */
2296 /* First we write the .au header. XXX Hope this is endian independant */
2297 /* the magic word 0x2e736e64 == .snd */
2298 /* XXX: Should we be checking for write errors below ? */
2299 phtonl(pd, 0x2e736e64);
2300 fwritten = ws_write(to_fd, pd, 4);
2301 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2305 destroy_progress_dlg(progbar);
2308 /* header offset == 24 bytes */
2310 fwritten = ws_write(to_fd, pd, 4);
2311 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2315 destroy_progress_dlg(progbar);
2318 /* total length, it is permited to set this to 0xffffffff */
2320 fwritten = ws_write(to_fd, pd, 4);
2321 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2325 destroy_progress_dlg(progbar);
2328 /* encoding format == 16-bit linear PCM */
2330 fwritten = ws_write(to_fd, pd, 4);
2331 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2335 destroy_progress_dlg(progbar);
2338 /* sample rate == 8000 Hz */
2340 fwritten = ws_write(to_fd, pd, 4);
2341 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2345 destroy_progress_dlg(progbar);
2350 fwritten = ws_write(to_fd, pd, 4);
2351 if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
2355 destroy_progress_dlg(progbar);
2360 /* only forward direction */
2361 case SAVE_FORWARD_DIRECTION_MASK: {
2362 progbar_count = user_data->forward.saveinfo.count;
2363 progbar_quantum = user_data->forward.saveinfo.count/100;
2364 while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
2367 if((count > progbar_nextstep) && (count <= progbar_count)) {
2368 update_progress_dlg(progbar,
2369 (gfloat) count/progbar_count, "Saving");
2370 progbar_nextstep = progbar_nextstep + progbar_quantum;
2374 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2375 sample = ulaw2linear(*f_pd);
2378 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2379 sample = alaw2linear(*f_pd);
2386 destroy_progress_dlg(progbar);
2390 fwritten = ws_write(to_fd, pd, 2);
2391 if ((fwritten < 2) || (fwritten < 0) || (fread_cnt < 0)) {
2395 destroy_progress_dlg(progbar);
2401 /* only reversed direction */
2402 case SAVE_REVERSE_DIRECTION_MASK: {
2403 progbar_count = user_data->reversed.saveinfo.count;
2404 progbar_quantum = user_data->reversed.saveinfo.count/100;
2405 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2408 if((count > progbar_nextstep) && (count <= progbar_count)) {
2409 update_progress_dlg(progbar,
2410 (gfloat) count/progbar_count, "Saving");
2411 progbar_nextstep = progbar_nextstep + progbar_quantum;
2415 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2416 sample = ulaw2linear(*r_pd);
2419 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2420 sample = alaw2linear(*r_pd);
2427 destroy_progress_dlg(progbar);
2431 rwritten = ws_write(to_fd, pd, 2);
2432 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2436 destroy_progress_dlg(progbar);
2442 /* both directions */
2443 case SAVE_BOTH_DIRECTION_MASK: {
2444 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2445 (progbar_count = user_data->forward.saveinfo.count) :
2446 (progbar_count = user_data->reversed.saveinfo.count);
2447 progbar_quantum = progbar_count/100;
2448 /* since conversation in one way can start later than in the other one,
2449 * we have to write some silence information for one channel */
2450 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2451 f_write_silence = (guint32)
2452 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2454 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2455 r_write_silence = (guint32)
2456 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2461 if((count > progbar_nextstep) && (count <= progbar_count)) {
2462 update_progress_dlg(progbar,
2463 (gfloat) count/progbar_count, "Saving");
2464 progbar_nextstep = progbar_nextstep + progbar_quantum;
2467 if(f_write_silence > 0) {
2468 rread = read(rev_fd, r_pd, 1);
2469 switch (user_data->forward.statinfo.reg_pt) {
2470 case AST_FORMAT_ULAW:
2471 *f_pd = SILENCE_PCMU;
2473 case AST_FORMAT_ALAW:
2474 *f_pd = SILENCE_PCMA;
2480 else if(r_write_silence > 0) {
2481 fread_cnt = read(forw_fd, f_pd, 1);
2482 switch (user_data->reversed.statinfo.reg_pt) {
2483 case AST_FORMAT_ULAW:
2484 *r_pd = SILENCE_PCMU;
2486 case AST_FORMAT_ALAW:
2487 *r_pd = SILENCE_PCMA;
2494 fread_cnt = read(forw_fd, f_pd, 1);
2495 rread = read(rev_fd, r_pd, 1);
2497 if ((rread == 0) && (fread_cnt == 0))
2499 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2500 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2503 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2504 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2512 destroy_progress_dlg(progbar);
2517 rwritten = ws_write(to_fd, pd, 2);
2518 if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
2522 destroy_progress_dlg(progbar);
2529 else if (format == SAVE_RAW_FORMAT) /* raw format */
2533 /* only forward direction */
2534 case SAVE_FORWARD_DIRECTION_MASK: {
2535 progbar_count = user_data->forward.saveinfo.count;
2536 progbar_quantum = user_data->forward.saveinfo.count/100;
2540 /* only reversed direction */
2541 case SAVE_REVERSE_DIRECTION_MASK: {
2542 progbar_count = user_data->reversed.saveinfo.count;
2543 progbar_quantum = user_data->reversed.saveinfo.count/100;
2551 destroy_progress_dlg(progbar);
2558 /* XXX how do you just copy the file? */
2559 while ((rread = read(fd, pd, 1)) > 0) {
2562 if((count > progbar_nextstep) && (count <= progbar_count)) {
2563 update_progress_dlg(progbar,
2564 (gfloat) count/progbar_count, "Saving");
2565 progbar_nextstep = progbar_nextstep + progbar_quantum;
2569 rwritten = ws_write(to_fd, pd, 1);
2571 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2575 destroy_progress_dlg(progbar);
2581 destroy_progress_dlg(progbar);
2589 /****************************************************************************/
2590 /* the user wants to save in a file */
2591 /* XXX support for different formats is currently commented out */
2592 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2595 /*GtkWidget *wav, *sw;*/
2596 GtkWidget *au, *raw;
2597 GtkWidget *rev, *forw, *both;
2598 user_data_t *user_data;
2599 gint channels , format;
2601 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2603 /* Perhaps the user specified a directory instead of a file.
2604 Check whether they did. */
2605 if (test_for_directory(g_dest) == EISDIR) {
2606 /* It's a directory - set the file selection box to display it. */
2607 set_last_open_dir(g_dest);
2608 file_selection_set_current_folder(fc, get_last_open_dir());
2609 gtk_file_chooser_set_current_name(fc, "");
2611 return FALSE; /* run the dialog again */
2615 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2616 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2618 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2619 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2620 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2621 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2622 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2623 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2625 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2626 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2627 * disable the ok button or disable the buttons for direction if only one is not ok. The
2628 * problem is if we open the save voice dialog and then click the refresh button and maybe
2629 * the state changes, so we can't save anymore. In this case we should be able to update
2630 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2633 /* we can not save in both directions */
2634 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2635 /* there are many combinations here, we just exit when first matches */
2636 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2637 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2638 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2639 "Can't save in a file: Unsupported codec!");
2640 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2641 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2642 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2643 "Can't save in a file: Wrong length of captured packets!");
2644 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2645 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2646 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2647 "Can't save in a file: Not all data in all packets was captured!");
2649 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2650 "Can't save in a file: File I/O problem!");
2652 return TRUE; /* we're done */
2654 /* we can not save forward direction */
2655 else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
2656 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2657 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2658 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2659 "Can't save forward direction in a file: Unsupported codec!");
2660 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2661 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2662 "Can't save forward direction in a file: Wrong length of captured packets!");
2663 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2664 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2665 "Can't save forward direction in a file: Not all data in all packets was captured!");
2667 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2668 "Can't save forward direction in a file: File I/O problem!");
2670 return TRUE; /* we're done */
2672 /* we can not save reversed direction */
2673 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
2674 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2675 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2676 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2677 "Can't save reversed direction in a file: Unsupported codec!");
2678 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2679 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2680 "Can't save reversed direction in a file: Wrong length of captured packets!");
2681 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2682 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2683 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2684 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2685 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2686 "Can't save reversed direction in a file: No IAX2 data!");
2688 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2689 "Can't save reversed direction in a file: File I/O problem!");
2691 return TRUE; /* we're done */
2695 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2696 format = SAVE_WAV_FORMAT;
2699 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2700 format = SAVE_AU_FORMAT;
2702 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2703 format = SAVE_SW_FORMAT;
2705 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2706 format = SAVE_RAW_FORMAT;
2708 format = SAVE_NONE_FORMAT;
2710 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2711 channels = SAVE_REVERSE_DIRECTION_MASK;
2712 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2713 channels = SAVE_BOTH_DIRECTION_MASK;
2715 channels = SAVE_FORWARD_DIRECTION_MASK;
2717 /* direction/format validity*/
2718 if (format == SAVE_AU_FORMAT)
2720 /* make sure streams are alaw/ulaw */
2721 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2722 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2723 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2725 return TRUE; /* we're done */
2727 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2728 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2729 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2731 return TRUE; /* we're done */
2733 /* make sure pt's don't differ */
2734 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2735 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2736 "Can't save in a file: Forward and reverse direction differ in type");
2738 return TRUE; /* we're done */
2741 else if (format == SAVE_RAW_FORMAT)
2743 /* can't save raw in both directions */
2744 if (channels == SAVE_BOTH_DIRECTION_MASK){
2745 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2746 "Can't save in a file: Unable to save raw data in both directions");
2748 return TRUE; /* we're done */
2753 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2754 "Can't save in a file: Invalid save format");
2756 return TRUE; /* we're done */
2759 if(!copy_file(g_dest, channels, format, user_data)) {
2760 /* XXX - report the error type! */
2761 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2762 "An error occurred while saving voice in a file!");
2764 return TRUE; /* we're done */
2767 return TRUE; /* we're done */
2770 /****************************************************************************/
2771 /* when the user wants to save the voice information in a file */
2772 /* XXX support for different formats is currently commented out */
2773 static void save_voice_as_cb(GtkWidget *bt _U_, gpointer data)
2775 user_data_t *user_data = data;
2778 GtkWidget *label_format;
2779 GtkWidget *channels_label;
2780 GtkWidget *forward_rb;
2781 GtkWidget *reversed_rb;
2783 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2787 /* if we can't save in a file: wrong codec, cut packets or other errors */
2788 /* should the error arise here or later when you click ok button ?
2789 * if we do it here, then we must disable the refresh button, so we don't do it here */
2791 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2792 if (user_data->dlg.save_voice_as_w != NULL) {
2793 /* There's already a Save voice info dialog box; reactivate it. */
2794 reactivate_window(user_data->dlg.save_voice_as_w);
2798 /* XXX - use file_selection from dlg_utils instead! */
2799 user_data->dlg.save_voice_as_w =
2800 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2801 GTK_WINDOW(user_data->dlg.notebook),
2802 GTK_FILE_CHOOSER_ACTION_SAVE,
2803 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2804 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2806 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2807 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2809 /* Container for each row of widgets */
2810 vertb = gtk_vbox_new(FALSE, 0);
2811 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2812 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2813 gtk_widget_show (vertb);
2815 table1 = gtk_table_new (2, 4, FALSE);
2816 gtk_widget_show (table1);
2817 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2818 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2819 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2822 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2823 gtk_widget_show (label_format);
2824 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2825 (GtkAttachOptions) (GTK_FILL),
2826 (GtkAttachOptions) (0), 0, 0);
2829 label_format = gtk_label_new ("Format: ");
2830 gtk_widget_show (label_format);
2831 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2832 (GtkAttachOptions) (GTK_FILL),
2833 (GtkAttachOptions) (0), 0, 0);
2835 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2837 raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2838 gtk_widget_show (raw_rb);
2839 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2840 (GtkAttachOptions) (GTK_FILL),
2841 (GtkAttachOptions) (0), 0, 0);
2844 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2845 gtk_widget_show (au_rb);
2846 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2847 (GtkAttachOptions) (GTK_FILL),
2848 (GtkAttachOptions) (0), 0, 0);
2851 /* we support .au - ulaw*/
2852 wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
2853 gtk_widget_show (wav_rb);
2854 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2855 (GtkAttachOptions) (GTK_FILL),
2856 (GtkAttachOptions) (0), 0, 0);
2858 sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
2859 gtk_widget_show (sw_rb);
2860 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2861 (GtkAttachOptions) (GTK_FILL),
2862 (GtkAttachOptions) (0), 0, 0);
2863 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2864 gtk_widget_show (au_rb);
2865 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2866 (GtkAttachOptions) (GTK_FILL),
2867 (GtkAttachOptions) (0), 0, 0);
2870 channels_label = gtk_label_new ("Channels: ");
2871 gtk_widget_show (channels_label);
2872 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2873 (GtkAttachOptions) (GTK_FILL),
2874 (GtkAttachOptions) (0), 0, 0);
2875 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2877 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2878 gtk_widget_show (forward_rb);
2879 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2880 (GtkAttachOptions) (GTK_FILL),
2881 (GtkAttachOptions) (0), 0, 0);
2883 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2884 gtk_widget_show (reversed_rb);
2885 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2886 (GtkAttachOptions) (GTK_FILL),
2887 (GtkAttachOptions) (0), 0, 0);
2889 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2890 gtk_widget_show (both_rb);
2891 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2892 (GtkAttachOptions) (GTK_FILL),
2893 (GtkAttachOptions) (0), 0, 0);
2895 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2898 /* if one direction is nok we don't allow saving
2899 XXX this is not ok since the user can click the refresh button and cause changes
2900 but we can not update this window. So we move all the decision on the time the ok
2901 button is clicked */
2902 if (user_data->forward.saved == FALSE) {
2903 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2904 gtk_widget_set_sensitive(forward_rb, FALSE);
2905 gtk_widget_set_sensitive(both_rb, FALSE);
2907 else if (user_data->reversed.saved == FALSE) {
2908 gtk_widget_set_sensitive(reversed_rb, FALSE);
2909 gtk_widget_set_sensitive(both_rb, FALSE);
2913 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2914 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2915 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2916 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2917 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2918 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2919 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2920 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2922 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2923 G_CALLBACK(window_delete_event_cb), NULL);
2924 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2925 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2927 gtk_widget_show(user_data->dlg.save_voice_as_w);
2928 window_present(user_data->dlg.save_voice_as_w);
2930 /* "Run" the GtkFileChooserDialog. */
2931 /* Upon exit: If "Accept" run the OK callback. */
2932 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2933 /* Destroy the window. */
2934 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2935 /* return with a TRUE status so that the dialog window will be destroyed. */
2936 /* Trying to re-run the dialog after popping up an alert box will not work */
2937 /* since the user will not be able to dismiss the alert box. */
2938 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2939 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2941 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2942 /* GtkFileChooserDialog. */
2943 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2944 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2945 break; /* we're done */
2948 window_destroy(user_data->dlg.save_voice_as_w);
2952 /****************************************************************************/
2953 /* when we are finished with redisection, we add the label for the statistic */
2954 static void draw_stat(user_data_t *user_data)
2956 gchar label_max[200];
2958 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2959 user_data->forward.statinfo.total_nr,
2960 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2962 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2964 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2965 user_data->reversed.statinfo.total_nr,
2966 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2968 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2975 /****************************************************************************/
2976 /* append a line to list */
2977 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2978 double delta, double jitter, double bandwidth, gchar *status,
2979 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2981 GtkListStore *list_store;
2983 if (strcmp(status, OK_TEXT) != 0) {
2984 user_data->dlg.number_of_nok++;
2987 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2989 /* Creates a new row at position. iter will be changed to point to this new row.
2990 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2991 * The row will be filled with the values given to this function.
2993 * should generally be preferred when inserting rows in a sorted list store.
2995 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2996 PACKET_COLUMN, number,
2997 DELTA_COLUMN, delta,
2998 JITTER_COLUMN, jitter,
2999 IPBW_COLUMN, bandwidth,
3000 STATUS_COLUMN, (char *)status,
3001 DATE_COLUMN, (char *)timeStr,
3002 LENGTH_COLUMN, pkt_len,
3003 FOREGROUND_COLOR_COL, NULL,
3004 BACKGROUND_COLOR_COL, (char *)color_str,
3007 if(flags & STAT_FLAG_FIRST){
3008 /* Set first row as active */
3009 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3013 /****************************************************************************
3014 * Functions needed to present values from the list
3017 /* Present floats with two decimals */
3019 iax2_float_data_func (GtkTreeViewColumn *column _U_,
3020 GtkCellRenderer *renderer,
3021 GtkTreeModel *model,
3029 /* the col to get data from is in userdata */
3030 gint float_col = GPOINTER_TO_INT(user_data);
3032 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
3034 /* save the current locale */
3035 savelocale = setlocale(LC_NUMERIC, NULL);
3036 /* switch to "C" locale to avoid problems with localized decimal separators
3037 * in g_snprintf("%f") functions
3039 setlocale(LC_NUMERIC, "C");
3041 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
3042 /* restore previous locale setting */
3043 setlocale(LC_NUMERIC, savelocale);
3045 g_object_set(renderer, "text", buf, NULL);
3051 GtkWidget* create_list(user_data_t* user_data)
3054 GtkListStore *list_store;
3056 GtkTreeViewColumn *column;
3057 GtkCellRenderer *renderer;
3058 GtkTreeSortable *sortable;
3059 GtkTreeView *list_view;
3060 GtkTreeSelection *selection;
3062 /* Create the store */
3063 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
3064 G_TYPE_UINT, /* Packet */
3065 G_TYPE_FLOAT, /* Delta(ms) */
3066 G_TYPE_FLOAT, /* Jitter(ms) */
3067 G_TYPE_FLOAT, /* IP BW(kbps) */
3068 G_TYPE_STRING, /* Status */
3069 G_TYPE_STRING, /* Date */
3070 G_TYPE_UINT, /* Length */
3071 G_TYPE_STRING, /* Foreground color */
3072 G_TYPE_STRING); /* Background color */
3075 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3077 list_view = GTK_TREE_VIEW(list);
3078 sortable = GTK_TREE_SORTABLE(list_store);
3080 /* Speed up the list display */
3081 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3083 /* Setup the sortable columns */
3084 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3085 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3087 /* The view now holds a reference. We can get rid of our own reference */
3088 g_object_unref (G_OBJECT (list_store));
3091 * Create the first column packet, associating the "text" attribute of the
3092 * cell_renderer to the first column of the model
3094 renderer = gtk_cell_renderer_text_new ();
3095 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3096 "text", PACKET_COLUMN,
3097 "foreground", FOREGROUND_COLOR_COL,
3098 "background", BACKGROUND_COLOR_COL,
3100 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3101 gtk_tree_view_column_set_resizable(column, TRUE);
3102 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3103 gtk_tree_view_column_set_min_width(column, 100);
3105 /* Add the column to the view. */
3106 gtk_tree_view_append_column (list_view, column);
3108 /* Second column.. Delta(ms). */
3109 renderer = gtk_cell_renderer_text_new ();
3110 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3111 "text", DELTA_COLUMN,
3112 "foreground", FOREGROUND_COLOR_COL,
3113 "background", BACKGROUND_COLOR_COL,
3116 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3117 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3119 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3120 gtk_tree_view_column_set_resizable(column, TRUE);
3121 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3122 gtk_tree_view_column_set_min_width(column, 100);
3123 gtk_tree_view_append_column (list_view, column);
3125 /* Third column.. Jitter(ms). */
3126 renderer = gtk_cell_renderer_text_new ();
3127 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3128 "text", JITTER_COLUMN,
3129 "foreground", FOREGROUND_COLOR_COL,
3130 "background", BACKGROUND_COLOR_COL,
3133 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3134 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3136 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3137 gtk_tree_view_column_set_resizable(column, TRUE);
3138 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3139 gtk_tree_view_column_set_min_width(column, 100);
3140 gtk_tree_view_append_column (list_view, column);
3142 /* Fourth column.. IP BW(kbps). */
3143 renderer = gtk_cell_renderer_text_new ();
3144 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3145 "text", IPBW_COLUMN,
3146 "foreground", FOREGROUND_COLOR_COL,
3147 "background", BACKGROUND_COLOR_COL,
3150 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3151 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3153 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3154 gtk_tree_view_column_set_resizable(column, TRUE);
3155 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3156 gtk_tree_view_column_set_min_width(column, 100);
3157 gtk_tree_view_append_column (list_view, column);
3159 /* Fifth column.. Status. */
3160 renderer = gtk_cell_renderer_text_new ();
3161 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3162 "text", STATUS_COLUMN,
3163 "foreground", FOREGROUND_COLOR_COL,
3164 "background", BACKGROUND_COLOR_COL,
3166 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3167 gtk_tree_view_column_set_resizable(column, TRUE);
3168 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3169 gtk_tree_view_column_set_min_width(column, 100);
3170 gtk_tree_view_append_column (list_view, column);
3172 /* Sixth column.. Length. */
3173 renderer = gtk_cell_renderer_text_new ();
3174 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3175 "text", LENGTH_COLUMN,
3176 "foreground", FOREGROUND_COLOR_COL,
3177 "background", BACKGROUND_COLOR_COL,
3181 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3182 gtk_tree_view_column_set_resizable(column, TRUE);
3183 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3184 gtk_tree_view_column_set_min_width(column, 100);
3185 gtk_tree_view_append_column (list_view, column);
3187 /* Now enable the sorting of each column */
3188 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3189 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3191 /* Setup the selection handler */
3192 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3193 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3195 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3196 G_CALLBACK (on_list_select_row),
3203 /****************************************************************************/
3204 /* Create the dialog box with all widgets */
3205 static void create_iax2_dialog(user_data_t* user_data)
3207 GtkWidget *window = NULL;
3208 GtkWidget *list_fwd;
3209 GtkWidget *list_rev;
3210 GtkWidget *label_stats_fwd;
3211 GtkWidget *label_stats_rev;
3212 GtkWidget *notebook;
3214 GtkWidget *main_vb, *page, *page_r;
3216 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3217 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3218 GtkWidget *graph_bt;
3219 gchar label_forward[150];
3220 gchar label_reverse[150];
3222 gchar str_ip_src[16];
3223 gchar str_ip_dst[16];
3225 window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
3226 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3228 /* Container for each row of widgets */
3229 main_vb = gtk_vbox_new(FALSE, 2);
3230 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3231 gtk_container_add(GTK_CONTAINER(window), main_vb);
3232 gtk_widget_show(main_vb);
3235 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3236 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3238 g_snprintf(label_forward, sizeof(label_forward),
3239 "Analysing stream from %s port %u to %s port %u ",
3240 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3243 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3244 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3246 g_snprintf(label_reverse, sizeof(label_reverse),
3247 "Analysing stream from %s port %u to %s port %u ",
3248 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3250 /* Start a notebook for flipping between sets of changes */
3251 notebook = gtk_notebook_new();
3252 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3253 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3255 user_data->dlg.notebook_signal_id =
3256 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3258 /* page for forward connection */
3259 page = gtk_vbox_new(FALSE, 8);
3260 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3262 /* direction label */
3263 label = gtk_label_new(label_forward);
3264 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3266 /* place for some statistics */
3267 label_stats_fwd = gtk_label_new("\n");
3268 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3270 /* scrolled window */
3271 scrolled_window = scrolled_window_new(NULL, NULL);
3274 list_fwd = create_list(user_data);
3275 gtk_widget_show(list_fwd);
3276 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3277 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3278 gtk_widget_show(scrolled_window);
3281 label = gtk_label_new(" Forward Direction ");
3282 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3284 /* same page for reversed connection */
3285 page_r = gtk_vbox_new(FALSE, 8);
3286 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3287 label = gtk_label_new(label_reverse);
3288 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3289 label_stats_rev = gtk_label_new("\n");
3290 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3292 scrolled_window_r = scrolled_window_new(NULL, NULL);
3294 list_rev = create_list(user_data);
3295 gtk_widget_show(list_rev);
3296 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3297 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3298 gtk_widget_show(scrolled_window_r);
3300 label = gtk_label_new(" Reversed Direction ");
3301 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3304 /* page for help&about or future */
3305 page_help = gtk_hbox_new(FALSE, 5);
3306 label = gtk_label_new(" Future ");
3307 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3308 frame = gtk_frame_new("");
3309 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3310 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3311 gtk_container_add(GTK_CONTAINER(frame), text);
3312 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3313 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3316 /* show all notebooks */
3317 gtk_widget_show_all(notebook);
3320 box4 = gtk_hbutton_box_new();
3321 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3322 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3323 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3324 gtk_box_set_spacing(GTK_BOX (box4), 0);
3325 gtk_widget_show(box4);
3327 voice_bt = gtk_button_new_with_label("Save payload...");
3328 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3329 gtk_widget_show(voice_bt);
3330 g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3332 csv_bt = gtk_button_new_with_label("Save as CSV...");
3333 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3334 gtk_widget_show(csv_bt);
3335 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3337 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3338 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3339 gtk_widget_show(refresh_bt);
3340 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3342 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3343 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3344 gtk_widget_show(goto_bt);
3345 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3347 graph_bt = gtk_button_new_with_label("Graph");
3348 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3349 gtk_widget_show(graph_bt);
3350 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3352 next_bt = gtk_button_new_with_label("Next non-Ok");
3353 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3354 gtk_widget_show(next_bt);
3355 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3357 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3358 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3359 #if GTK_CHECK_VERSION(2,18,0)
3360 gtk_widget_set_can_default(close_bt, TRUE);
3362 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3364 gtk_widget_show(close_bt);
3365 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3367 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3368 g_signal_connect(window, "destroy", G_CALLBACK(on_iax2_window_destroy), user_data);
3370 gtk_widget_show(window);
3371 window_present(window);
3373 /* some widget references need to be saved for outside use */
3374 user_data->dlg.window = window;
3375 user_data->dlg.list_fwd = list_fwd;
3376 user_data->dlg.list_rev = list_rev;
3377 user_data->dlg.label_stats_fwd = label_stats_fwd;
3378 user_data->dlg.label_stats_rev = label_stats_rev;
3379 user_data->dlg.notebook = notebook;
3380 user_data->dlg.selected_list = list_fwd;
3381 user_data->dlg.number_of_nok = 0;
3384 * select the initial row
3386 gtk_widget_grab_focus(list_fwd);
3390 /****************************************************************************/
3391 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3392 const gchar* proto_field, guint32* p_result)
3395 proto_node *proto_sibling_node;
3396 header_field_info *hfssrc;
3399 finfo = PNODE_FINFO(ptree_node);
3401 if (hfinformation==(finfo->hfinfo)) {
3402 hfssrc = proto_registrar_get_byname(proto_field);
3403 if (hfssrc == NULL) {
3404 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3408 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3409 ptree_node=ptree_node->next) {
3410 finfo=PNODE_FINFO(ptree_node);
3411 if (hfssrc==finfo->hfinfo) {
3412 if (hfinformation->type==FT_IPv4) {
3413 ipv4 = fvalue_get(&finfo->value);
3414 *p_result = ipv4_get_net_order_addr(ipv4);
3417 *p_result = fvalue_get_uinteger(&finfo->value);
3426 proto_sibling_node = ptree_node->next;
3428 if (proto_sibling_node) {
3429 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3435 /****************************************************************************/
3436 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3437 const gchar* proto_name,
3438 const gchar* proto_field,
3441 proto_node *ptree_node;
3442 header_field_info *hfinformation;
3444 hfinformation = proto_registrar_get_byname(proto_name);
3445 if (hfinformation == NULL) {
3446 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3451 ptree_node = ((proto_node *)protocol_tree)->first_child;
3453 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3457 return process_node(ptree_node, hfinformation, proto_field, p_result);
3461 /****************************************************************************/
3463 address *ip_src_fwd,
3464 guint16 port_src_fwd,
3465 address *ip_dst_fwd,
3466 guint16 port_dst_fwd,
3467 address *ip_src_rev,
3468 guint16 port_src_rev,
3469 address *ip_dst_rev,
3470 guint16 port_dst_rev
3473 user_data_t *user_data;
3476 static GdkColor col[MAX_GRAPHS] = {
3477 {0, 0x0000, 0x0000, 0x0000},
3478 {0, 0xffff, 0x0000, 0x0000},
3479 {0, 0x0000, 0xffff, 0x0000},
3480 {0, 0x0000, 0x0000, 0xffff}
3485 user_data = g_malloc(sizeof(user_data_t));
3487 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3488 user_data->port_src_fwd = port_src_fwd;
3489 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3490 user_data->port_dst_fwd = port_dst_fwd;
3491 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3492 user_data->port_src_rev = port_src_rev;
3493 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3494 user_data->port_dst_rev = port_dst_rev;
3497 /* file names for storing sound data */
3498 /*XXX: check for errors*/
3499 fd = create_tempfile(&tempname, "wireshark_iax2_f");
3500 user_data->f_tempname = g_strdup(tempname);
3502 fd = create_tempfile(&tempname, "wireshark_iax2_r");
3503 user_data->r_tempname = g_strdup(tempname);
3505 user_data->forward.saveinfo.fp = NULL;
3506 user_data->reversed.saveinfo.fp = NULL;
3507 user_data->dlg.save_voice_as_w = NULL;
3508 user_data->dlg.save_csv_as_w = NULL;
3509 user_data->dlg.dialog_graph.window = NULL;
3511 /* init dialog_graph */
3512 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3513 user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
3514 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
3515 user_data->dlg.dialog_graph.draw_area=NULL;
3516 #if GTK_CHECK_VERSION(2,22,0)
3517 user_data->dlg.dialog_graph.surface=NULL;
3519 user_data->dlg.dialog_graph.pixmap=NULL;
3521 user_data->dlg.dialog_graph.scrollbar=NULL;
3522 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3523 user_data->dlg.dialog_graph.surface_width=500;
3524 user_data->dlg.dialog_graph.surface_height=200;
3525 user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
3526 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3527 user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
3528 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3529 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3530 user_data->dlg.dialog_graph.max_interval=0;
3531 user_data->dlg.dialog_graph.num_items=0;
3532 user_data->dlg.dialog_graph.start_time = -1;
3534 for(i=0;i<MAX_GRAPHS;i++){
3535 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3536 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3537 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3538 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3539 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3540 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3541 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3544 /* create the dialog box */
3545 create_iax2_dialog(user_data);
3547 /* proceed as if the Refresh button would have been pressed */
3548 on_refresh_bt_clicked(NULL, user_data);
3551 /****************************************************************************/
3552 /* entry point from main menu */
3553 #ifdef MAIN_MENU_USE_UIMANAGER
3554 void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3556 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3560 guint16 port_src_fwd;
3562 guint16 port_dst_fwd;
3564 guint16 port_src_rev;
3566 guint16 port_dst_rev;
3567 /* unsigned int ptype; */
3569 gchar filter_text[256];
3573 gboolean frame_matched;
3575 GList *strinfo_list;
3576 GList *filtered_list = NULL;
3577 rtp_stream_info_t *strinfo;
3580 /* Try to compile the filter. */
3581 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3582 if (!dfilter_compile(filter_text, &sfcode)) {
3583 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3586 /* we load the current file into cf variable */
3588 fdata = cf->current_frame;
3590 /* we are on the selected frame now */
3592 return; /* if we exit here it's an error */
3594 /* dissect the current frame */
3595 if (!cf_read_frame(cf, fdata))
3596 return; /* error reading the frame */
3597 epan_dissect_init(&edt, TRUE, FALSE);
3598 epan_dissect_prime_dfilter(&edt, sfcode);
3599 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3601 /* if it is not an iax2 frame, show an error dialog */
3602 frame_matched = dfilter_apply_edt(sfcode, &edt);
3603 if (frame_matched != 1) {
3604 epan_dissect_cleanup(&edt);
3605 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3606 "You didn't choose a IAX2 packet!");
3609 /* check if it is Voice or MiniPacket
3610 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3611 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3612 "Please select a Voice packet!");
3616 /* check if it is part of a Call */
3617 if (edt.pi.circuit_id == 0) {
3618 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3619 "Please select a Call packet!");
3623 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3624 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3625 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3626 port_src_fwd = edt.pi.srcport;
3627 port_dst_fwd = edt.pi.destport;
3629 /* assume the inverse ip/port combination for the reverse direction */
3630 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3631 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3632 port_src_rev = edt.pi.destport;
3633 port_dst_rev = edt.pi.srcport;
3635 /* Scan for rtpstream */
3637 /* search for reversed direction in the global rtp streams list */
3639 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3640 while (strinfo_list)
3642 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3643 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3644 && strinfo->src_port==port_src_fwd
3645 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3646 && strinfo->dest_port==port_dst_fwd)
3648 filtered_list = g_list_prepend(filtered_list, strinfo);
3651 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3652 && strinfo->src_port==port_src_rev
3653 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3654 && strinfo->dest_port==port_dst_rev)
3657 filtered_list = g_list_append(filtered_list, strinfo);
3660 strinfo_list = g_list_next(strinfo_list);
3663 /* if more than one reverse streams found, we let the user choose the right one */
3665 rtpstream_dlg_show(filtered_list);
3682 /****************************************************************************/
3684 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3686 iax2_analysis_cb(NULL, NULL);
3689 /****************************************************************************/
3691 register_tap_listener_iax2_analysis(void)
3693 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3695 #ifdef MAIN_MENU_USE_UIMANAGER
3697 register_stat_menu_item("IA_X2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3698 iax2_analysis_cb, NULL, NULL, NULL);
3709 * indent-tabs-mode: t
3712 * ex: set shiftwidth=8 tabstop=8 noexpandtab
3713 * :indentSize=8:tabSize=8:noTabs=false: