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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
54 #include <epan/epan_dissect.h>
55 #include <epan/filesystem.h>
56 #include <epan/pint.h>
58 #include <epan/tap-voip.h>
59 #include <epan/dissectors/packet-iax2.h>
60 #include <epan/iax2_codec_type.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
67 #include "../stat_menu.h"
68 #include "../tempfile.h"
70 #include "ui/alert_box.h"
71 #include "ui/last_open_dir.h"
72 #include "ui/progress_dlg.h"
73 #include "ui/simple_dialog.h"
74 #include "ui/utf8_entities.h"
76 #include <wsutil/file_util.h>
78 #include "ui/gtk/gtkglobals.h"
79 #include "ui/gtk/dlg_utils.h"
80 #include "ui/gtk/file_dlg.h"
81 #include "ui/gtk/gui_utils.h"
82 #include "ui/gtk/gui_stat_menu.h"
83 #include "ui/gtk/main.h"
84 #include "ui/gtk/rtp_analysis.h"
85 #include "ui/gtk/iax2_analysis.h"
86 #include "ui/gtk/rtp_stream.h"
87 #include "ui/gtk/rtp_stream_dlg.h"
89 #include "ui/gtk/old-gtk-compat.h"
100 FOREGROUND_COLOR_COL,
101 BACKGROUND_COLOR_COL,
102 N_COLUMN /* The number of columns */
105 /****************************************************************************/
107 #define NUM_GRAPH_ITEMS 100000
108 #define MAX_YSCALE 16
109 #define DEFAULT_YSCALE_INDEX 0 /* AUTO_MAX_YSCALE */
110 #define AUTO_MAX_YSCALE 0
112 #define GRAPH_FWD_JITTER 0
113 #define GRAPH_FWD_DIFF 1
114 #define GRAPH_REV_JITTER 2
115 #define GRAPH_REV_DIFF 3
116 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
118 #define MAX_PIXELS_PER_TICK 4
119 #define DEFAULT_PIXELS_PER_TICK_INDEX 1
120 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
121 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
123 #define MAX_TICK_VALUES 5
124 #define DEFAULT_TICK_VALUE_INDEX 1
125 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
126 typedef struct _dialog_graph_graph_item_t {
129 } dialog_graph_graph_item_t;
131 typedef struct _dialog_graph_graph_t {
132 struct _user_data_t *ud;
133 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
136 GtkWidget *display_button;
138 #if GTK_CHECK_VERSION(3,0,0)
143 } dialog_graph_graph_t;
146 typedef struct _dialog_graph_t {
147 gboolean needs_redraw;
148 gint32 interval; /* measurement interval in ms */
149 guint32 last_interval;
150 guint32 max_interval; /* XXX max_interval and num_items are redundant */
154 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
156 GtkWidget *draw_area;
157 #if GTK_CHECK_VERSION(2,22,0)
158 cairo_surface_t *surface;
162 GtkAdjustment *scrollbar_adjustment;
163 GtkWidget *scrollbar;
170 typedef struct _dialog_data_t {
175 GtkWidget *label_stats_fwd;
176 GtkWidget *label_stats_rev;
177 GtkWidget *selected_list;
179 GtkTreeSelection *selected_list_sel;
180 gint selected_list_row;
182 GtkWidget *save_voice_as_w;
183 GtkWidget *save_csv_as_w;
184 gint notebook_signal_id;
185 dialog_graph_t dialog_graph;
188 #define OK_TEXT "[ Ok ]"
190 /* type of error when saving voice in a file didn't succeed */
193 TAP_RTP_WRONG_LENGTH,
194 TAP_RTP_PADDING_ERROR,
196 TAP_RTP_FILE_OPEN_ERROR,
197 TAP_RTP_FILE_WRITE_ERROR,
201 typedef struct _tap_iax2_save_info_t {
204 error_type_t error_type;
206 } tap_iax2_save_info_t;
209 /* structure that holds the information about the forward and reversed direction */
210 struct _info_direction {
211 tap_iax2_stat_t statinfo;
212 tap_iax2_save_info_t saveinfo;
215 #define SILENCE_PCMU (guint8)0xFF
216 #define SILENCE_PCMA (guint8)0x55
218 /* structure that holds general information about the connection
219 * and structures for both directions */
220 typedef struct _user_data_t {
221 /* tap associated data*/
223 guint16 port_src_fwd;
225 guint16 port_dst_fwd;
227 guint16 port_src_rev;
229 guint16 port_dst_rev;
231 struct _info_direction forward;
232 struct _info_direction reversed;
237 /* dialog associated data */
244 static const gchar *titles[7] = {
254 #define SAVE_FORWARD_DIRECTION_MASK 0x01
255 #define SAVE_REVERSE_DIRECTION_MASK 0x02
256 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
258 #define SAVE_NONE_FORMAT 0
259 #define SAVE_WAV_FORMAT 1
260 #define SAVE_AU_FORMAT 2
261 #define SAVE_SW_FORMAT 3
262 #define SAVE_RAW_FORMAT 4
265 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data);
266 /****************************************************************************/
267 static void enable_graph(dialog_graph_graph_t *dgg)
274 static void dialog_graph_reset(user_data_t* user_data);
278 /****************************************************************************/
281 /****************************************************************************/
282 /* when there is a [re]reading of packet's */
284 iax2_reset(void *user_data_arg)
286 user_data_t *user_data = user_data_arg;
287 user_data->forward.statinfo.first_packet = TRUE;
288 user_data->reversed.statinfo.first_packet = TRUE;
289 user_data->forward.statinfo.max_delta = 0;
290 user_data->reversed.statinfo.max_delta = 0;
291 user_data->forward.statinfo.max_jitter = 0;
292 user_data->reversed.statinfo.max_jitter = 0;
293 user_data->forward.statinfo.mean_jitter = 0;
294 user_data->reversed.statinfo.mean_jitter = 0;
295 user_data->forward.statinfo.delta = 0;
296 user_data->reversed.statinfo.delta = 0;
297 user_data->forward.statinfo.diff = 0;
298 user_data->reversed.statinfo.diff = 0;
299 user_data->forward.statinfo.jitter = 0;
300 user_data->reversed.statinfo.jitter = 0;
301 user_data->forward.statinfo.bandwidth = 0;
302 user_data->reversed.statinfo.bandwidth = 0;
303 user_data->forward.statinfo.total_bytes = 0;
304 user_data->reversed.statinfo.total_bytes = 0;
305 user_data->forward.statinfo.bw_start_index = 0;
306 user_data->reversed.statinfo.bw_start_index = 0;
307 user_data->forward.statinfo.bw_index = 0;
308 user_data->reversed.statinfo.bw_index = 0;
309 user_data->forward.statinfo.timestamp = 0;
310 user_data->reversed.statinfo.timestamp = 0;
311 user_data->forward.statinfo.max_nr = 0;
312 user_data->reversed.statinfo.max_nr = 0;
313 user_data->forward.statinfo.total_nr = 0;
314 user_data->reversed.statinfo.total_nr = 0;
315 user_data->forward.statinfo.sequence = 0;
316 user_data->reversed.statinfo.sequence = 0;
317 user_data->forward.statinfo.start_seq_nr = 0;
318 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
319 user_data->forward.statinfo.stop_seq_nr = 0;
320 user_data->reversed.statinfo.stop_seq_nr = 0;
321 user_data->forward.statinfo.cycles = 0;
322 user_data->reversed.statinfo.cycles = 0;
323 user_data->forward.statinfo.under = FALSE;
324 user_data->reversed.statinfo.under = FALSE;
325 user_data->forward.statinfo.start_time = 0;
326 user_data->reversed.statinfo.start_time = 0;
327 user_data->forward.statinfo.time = 0;
328 user_data->reversed.statinfo.time = 0;
329 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
330 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
332 user_data->forward.saveinfo.count = 0;
333 user_data->reversed.saveinfo.count = 0;
334 user_data->forward.saveinfo.saved = FALSE;
335 user_data->reversed.saveinfo.saved = FALSE;
337 /* clear the dialog box lists */
338 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
339 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
341 /* reset graph info */
342 dialog_graph_reset(user_data);
345 /* XXX check for error at fclose? */
346 if (user_data->forward.saveinfo.fp != NULL)
347 fclose(user_data->forward.saveinfo.fp);
348 if (user_data->reversed.saveinfo.fp != NULL)
349 fclose(user_data->reversed.saveinfo.fp);
350 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
351 if (user_data->forward.saveinfo.fp == NULL)
352 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
353 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
354 if (user_data->reversed.saveinfo.fp == NULL)
355 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
359 /****************************************************************************/
360 static gboolean iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
362 dialog_graph_graph_item_t *it;
366 /* we sometimes get called when dgg is disabled.
367 this is a bug since the tap listener should be removed first */
372 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
375 * Find which interval this is supposed to go in and store the
376 * interval index as idx
378 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
379 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
381 rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
385 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
387 /* some sanity checks */
388 if(idx>=NUM_GRAPH_ITEMS){
392 /* update num_items */
393 if(idx > dgg->ud->dlg.dialog_graph.num_items){
394 dgg->ud->dlg.dialog_graph.num_items=idx;
395 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
399 * Find the appropriate dialog_graph_graph_item_t structure
404 * Use the max value to highlight RTP problems
406 if (value > it->value) {
409 it->flags = it->flags | statinfo->flags;
414 /****************************************************************************/
415 /* here we can redraw the output */
417 static void iax2_draw(void *prs _U_)
422 /* forward declarations */
423 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
424 double delta, double jitter, double bandwidth, gchar *status,
425 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
427 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
428 tap_iax2_stat_t *statinfo, packet_info *pinfo,
429 const struct _iax2_info_t *iax2info);
431 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
432 tap_iax2_stat_t *statinfo,
434 const struct _iax2_info_t *iax2info);
437 /****************************************************************************/
438 /* whenever a IAX2 packet is seen by the tap listener */
439 static gboolean iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
441 user_data_t *user_data = user_data_arg;
442 const struct _iax2_info_t *iax2info = iax2info_arg;
444 /* we ignore packets that are not displayed */
445 if (pinfo->fd->flags.passed_dfilter == 0)
448 /* we ignore packets that carry no data */
449 if (iax2info->payload_len == 0)
452 /* is it the forward direction? */
453 else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
454 && user_data->port_src_fwd == pinfo->srcport
455 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
456 && user_data->port_dst_fwd == pinfo->destport) {
457 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
458 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
459 &(user_data->forward.statinfo),
461 (guint32)(user_data->forward.statinfo.jitter*1000000));
462 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
463 &(user_data->forward.statinfo),
465 (guint32)(user_data->forward.statinfo.diff*1000000));
466 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
467 &(user_data->forward.statinfo), pinfo, iax2info);
468 iax2_packet_save_payload(&(user_data->forward.saveinfo),
469 &(user_data->forward.statinfo), pinfo, iax2info);
471 /* is it the reversed direction? */
472 else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
473 && user_data->port_src_rev == pinfo->srcport
474 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
475 && user_data->port_dst_rev == pinfo->destport) {
476 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
477 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
478 &(user_data->reversed.statinfo),
480 (guint32)(user_data->reversed.statinfo.jitter*1000000));
481 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
482 &(user_data->reversed.statinfo),
484 (guint32)(user_data->reversed.statinfo.diff*1000000));
485 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
486 &(user_data->reversed.statinfo), pinfo, iax2info);
487 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
488 &(user_data->reversed.statinfo), pinfo, iax2info);
494 /****************************************************************************/
495 /* This comes from tap-rtp-common.c */
496 /****************************************************************************/
498 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
500 const struct _iax2_info_t *iax2info)
503 double current_jitter;
507 /* check payload type */
508 if (iax2info->ftype == AST_FRAME_VOICE) {
509 if (iax2info->csub != statinfo->pt)
510 statinfo->flags |= STAT_FLAG_PT_CHANGE;
511 statinfo->pt = iax2info->csub;
514 /* store the current time and calculate the current jitter */
515 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
516 current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
517 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
518 statinfo->delta = current_time-(statinfo->time);
519 statinfo->jitter = current_jitter;
520 statinfo->diff = current_diff;
522 /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
523 statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
524 statinfo->bw_history[statinfo->bw_index].time = current_time;
525 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
526 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
527 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
528 statinfo->bw_start_index++;
529 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
531 statinfo->total_bytes += iax2info->payload_len + 24;
532 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
533 statinfo->bw_index++;
534 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
537 /* is this the first packet we got in this direction? */
538 if (statinfo->first_packet) {
539 statinfo->start_seq_nr = 0;
540 statinfo->start_time = current_time;
542 statinfo->jitter = 0;
544 statinfo->flags |= STAT_FLAG_FIRST;
545 statinfo->first_packet = FALSE;
547 /* is it a regular packet? */
548 if (!(statinfo->flags & STAT_FLAG_FIRST)
549 && !(statinfo->flags & STAT_FLAG_MARKER)
550 && !(statinfo->flags & STAT_FLAG_PT_CN)
551 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
552 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
553 /* include it in maximum delta calculation */
554 if (statinfo->delta > statinfo->max_delta) {
555 statinfo->max_delta = statinfo->delta;
556 statinfo->max_nr = pinfo->fd->num;
558 /* maximum and mean jitter calculation */
559 if (statinfo->jitter > statinfo->max_jitter) {
560 statinfo->max_jitter = statinfo->jitter;
562 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
564 /* regular payload change? (CN ignored) */
565 if (!(statinfo->flags & STAT_FLAG_FIRST)
566 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
567 if ((statinfo->pt != statinfo->reg_pt)
568 && (statinfo->reg_pt != PT_UNDEFINED)) {
569 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
573 /* set regular payload*/
574 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
575 statinfo->reg_pt = statinfo->pt;
578 /* TODO: lost packets / duplicated: we should infer this from timestamp... */
579 statinfo->time = current_time;
580 statinfo->timestamp = iax2info->timestamp; /* millisecs */
581 statinfo->stop_seq_nr = 0;
582 statinfo->total_nr++;
589 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
590 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
591 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
592 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
593 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
596 /****************************************************************************/
597 /* adds statistics information from the packet to the list */
598 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
599 tap_iax2_stat_t *statinfo, packet_info *pinfo,
600 const struct _iax2_info_t *iax2info _U_)
607 /* GdkColor color = COLOR_DEFAULT; */
609 then = pinfo->fd->abs_ts.secs;
610 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
611 tm_tmp = localtime(&then);
612 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
615 tm_tmp->tm_year + 1900,
621 /* Default to using black on white text if nothing below overrides it */
622 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
624 if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
625 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
626 /* color = COLOR_ERROR; */
627 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
629 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
630 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
631 /* color = COLOR_WARNING; */
632 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
634 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
635 g_snprintf(status,sizeof(status),"Incorrect timestamp");
636 /* color = COLOR_WARNING; */
637 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
639 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
640 && !(statinfo->flags & STAT_FLAG_FIRST)
641 && !(statinfo->flags & STAT_FLAG_PT_CN)
642 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
643 && !(statinfo->flags & STAT_FLAG_MARKER)) {
644 g_snprintf(status,sizeof(status),"Marker missing?");
645 /* color = COLOR_WARNING; */
646 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
649 if (statinfo->flags & STAT_FLAG_MARKER) {
650 /* color = COLOR_WARNING; */
651 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
653 g_snprintf(status,sizeof(status),OK_TEXT);
655 /* is this the first packet we got in this direction? */
656 if (statinfo->flags & STAT_FLAG_FIRST) {
657 add_to_list(list, user_data,
663 timeStr, pinfo->fd->pkt_len,
668 add_to_list(list, user_data,
670 statinfo->delta*1000,
671 statinfo->jitter*1000,
674 timeStr, pinfo->fd->pkt_len,
681 #define MAX_SILENCE_TICKS 1000000
682 /****************************************************************************/
683 static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
684 tap_iax2_stat_t *statinfo,
686 const struct _iax2_info_t *iax2info)
691 /* is this the first packet we got in this direction? */
692 if (statinfo->flags & STAT_FLAG_FIRST) {
693 if (saveinfo->fp == NULL) {
694 saveinfo->saved = FALSE;
695 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
698 saveinfo->saved = TRUE;
701 /* save the voice information */
702 /* if there was already an error, we quit */
703 if (saveinfo->saved == FALSE)
706 /* if the captured length and packet length aren't equal, we quit */
707 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
708 saveinfo->saved = FALSE;
709 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
713 if (iax2info->payload_len > 0) {
714 data = iax2info->payload_data;
715 nchars = fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
716 if (nchars != iax2info->payload_len) {
717 /* Write error or short write */
718 saveinfo->saved = FALSE;
719 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
722 saveinfo->count+=iax2info->payload_len;
724 if (fflush(saveinfo->fp) == EOF) {
726 saveinfo->saved = FALSE;
727 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
730 saveinfo->saved = TRUE;
738 /****************************************************************************/
741 /****************************************************************************/
743 /****************************************************************************/
744 /* close the dialog window and remove the tap listener */
745 static void on_iax2_window_destroy(GtkWidget *win _U_, gpointer data)
747 user_data_t *user_data = data;
749 /* remove tap listener */
750 remove_tap_listener(user_data);
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 #if GTK_CHECK_VERSION(3,0,0)
1407 static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
1409 user_data_t *user_data = data;
1410 GtkAllocation allocation;
1412 gtk_widget_get_allocation (widget, &allocation);
1414 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1415 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
1421 static gboolean draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1423 user_data_t *user_data = data;
1424 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1426 #if GTK_CHECK_VERSION(2,22,0)
1427 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1429 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1431 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1439 /****************************************************************************/
1440 static gboolean draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1442 user_data_t *user_data = data;
1443 GtkAllocation widget_alloc;
1446 #if GTK_CHECK_VERSION(2,22,0)
1447 if(user_data->dlg.dialog_graph.surface){
1448 g_object_unref(user_data->dlg.dialog_graph.surface);
1449 user_data->dlg.dialog_graph.surface=NULL;
1451 gtk_widget_get_allocation(widget, &widget_alloc);
1452 user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1453 CAIRO_CONTENT_COLOR,
1455 widget_alloc.height);
1457 if(user_data->dlg.dialog_graph.pixmap){
1458 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1459 user_data->dlg.dialog_graph.pixmap=NULL;
1462 gtk_widget_get_allocation(widget, &widget_alloc);
1463 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1465 widget_alloc.height,
1468 user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
1469 user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
1471 #if GTK_CHECK_VERSION(2,22,0)
1472 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1474 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1476 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1477 cairo_set_source_rgb (cr, 1, 1, 1);
1481 dialog_graph_redraw(user_data);
1485 /****************************************************************************/
1486 static void scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1488 user_data_t *user_data = data;
1491 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));
1492 if(user_data->dlg.dialog_graph.last_interval==mi){
1495 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1496 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1500 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1502 dialog_graph_redraw(user_data);
1506 /****************************************************************************/
1507 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1509 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1510 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), user_data);
1512 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);
1514 /* signals needed to handle backing pixmap */
1515 #if GTK_CHECK_VERSION(3,0,0)
1516 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
1518 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(draw_area_expose_event), user_data);
1520 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(draw_area_configure_event), user_data);
1522 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1523 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1525 /* create the associated scrollbar */
1526 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1527 user_data->dlg.dialog_graph.scrollbar=gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL,user_data->dlg.dialog_graph.scrollbar_adjustment);
1528 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1529 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1530 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1533 /****************************************************************************/
1534 static void disable_graph(dialog_graph_graph_t *dgg)
1538 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1543 /****************************************************************************/
1544 static void filter_box_display_button_cb(GtkWidget *widget _U_, gpointer data)
1546 dialog_graph_graph_t *dgg = data;
1548 /* this graph is not active, just update display and redraw */
1549 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1551 dialog_graph_redraw(dgg->ud);
1556 cf_retap_packets(&cfile);
1557 dialog_graph_redraw(dgg->ud);
1562 /****************************************************************************/
1563 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1569 hbox=ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1570 gtk_container_add(GTK_CONTAINER(box), hbox);
1571 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1572 gtk_widget_show(hbox);
1574 g_snprintf(str, sizeof(str), "Graph %d", num);
1575 dgg->display_button=gtk_toggle_button_new_with_label(str);
1576 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1577 gtk_widget_show(dgg->display_button);
1578 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1579 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_box_display_button_cb), dgg);
1581 label=gtk_label_new(dgg->title);
1582 gtk_widget_show(label);
1583 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1584 #if GTK_CHECK_VERSION(3,0,0)
1585 gtk_widget_override_color(label, GTK_STATE_FLAG_NORMAL, &dgg->rgba_color);
1586 /* XXX gtk_widget_override_color() takes flags not state */
1587 gtk_widget_override_color(label, GTK_STATE_ACTIVE, &dgg->rgba_color);
1588 gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &dgg->rgba_color);
1589 gtk_widget_override_color(label, GTK_STATE_SELECTED, &dgg->rgba_color);
1590 gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &dgg->rgba_color);
1592 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1593 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1594 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1595 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1596 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1601 /****************************************************************************/
1602 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1609 frame=gtk_frame_new("Graphs");
1610 gtk_container_add(GTK_CONTAINER(box), frame);
1611 gtk_widget_show(frame);
1613 vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 1, FALSE);
1614 gtk_container_add(GTK_CONTAINER(frame), vbox);
1615 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1616 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1617 gtk_widget_show(vbox);
1619 for(i=0;i<MAX_GRAPHS;i++){
1620 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1623 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1624 gtk_widget_show(label);
1625 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1630 /****************************************************************************/
1631 static void yscale_select(GtkWidget *item, gpointer data)
1633 user_data_t *user_data = data;
1636 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1638 user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1639 dialog_graph_redraw(user_data);
1642 /****************************************************************************/
1643 static void pixels_per_tick_select(GtkWidget *item, gpointer data)
1645 user_data_t *user_data = data;
1648 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1650 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1651 dialog_graph_redraw(user_data);
1654 /****************************************************************************/
1655 static void tick_interval_select(GtkWidget *item, gpointer data)
1657 user_data_t *user_data = data;
1660 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1662 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1663 cf_retap_packets(&cfile);
1664 dialog_graph_redraw(user_data);
1667 /****************************************************************************/
1669 create_yscale_max_menu_items(user_data_t* user_data)
1672 GtkWidget *combo_box;
1675 combo_box = gtk_combo_box_text_new();
1677 for(i=0;i<MAX_YSCALE;i++){
1678 if(yscale_max[i]==AUTO_MAX_YSCALE){
1679 g_strlcpy(str,"Auto",sizeof(str));
1680 } else if (yscale_max[i] < 1000000) {
1681 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1683 g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1685 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1687 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1688 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), user_data);
1693 /****************************************************************************/
1695 create_pixels_per_tick_menu_items(user_data_t *user_data)
1698 GtkWidget *combo_box;
1701 combo_box = gtk_combo_box_text_new();
1703 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1704 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1705 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1707 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1709 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), user_data);
1714 /****************************************************************************/
1716 create_tick_interval_menu_items(user_data_t *user_data)
1718 GtkWidget *combo_box;
1722 combo_box = gtk_combo_box_text_new();
1724 for(i=0;i<MAX_TICK_VALUES;i++){
1725 if(tick_interval_values[i]>=1000){
1726 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1727 } else if(tick_interval_values[i]>=100){
1728 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1729 } else if(tick_interval_values[i]>=10){
1730 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1732 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1734 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1736 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1737 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), user_data);
1742 /****************************************************************************/
1743 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1747 GtkWidget *combo_box;
1749 hbox=ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1750 gtk_container_add(GTK_CONTAINER(box), hbox);
1751 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1752 gtk_widget_show(hbox);
1754 label=gtk_label_new(name);
1755 gtk_widget_show(label);
1756 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1758 combo_box = (*func)(user_data);
1759 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1760 gtk_widget_show(combo_box);
1763 /****************************************************************************/
1764 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1766 GtkWidget *frame_vbox;
1770 frame_vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1771 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1772 gtk_widget_show(frame_vbox);
1774 frame = gtk_frame_new("X Axis");
1775 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1776 gtk_widget_show(frame);
1778 vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1779 gtk_container_add(GTK_CONTAINER(frame), vbox);
1780 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1781 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1782 gtk_widget_show(vbox);
1784 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1785 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1787 frame = gtk_frame_new("Y Axis");
1788 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1789 gtk_widget_show(frame);
1791 vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1792 gtk_container_add(GTK_CONTAINER(frame), vbox);
1793 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1794 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1795 gtk_widget_show(vbox);
1797 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1802 /****************************************************************************/
1803 static void dialog_graph_init_window(user_data_t* user_data)
1807 GtkWidget *bt_close;
1809 /* create the main window */
1810 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1812 vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1813 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1814 gtk_widget_show(vbox);
1816 create_draw_area(user_data, vbox);
1818 hbox=ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1819 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1820 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1821 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1822 gtk_widget_show(hbox);
1824 create_filter_area(user_data, hbox);
1825 create_ctrl_area(user_data, hbox);
1827 dialog_graph_set_title(user_data);
1829 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1830 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1831 gtk_widget_show(hbox);
1833 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1834 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1836 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1838 gtk_widget_show(user_data->dlg.dialog_graph.window);
1839 window_present(user_data->dlg.dialog_graph.window);
1844 /****************************************************************************/
1845 static void on_graph_bt_clicked(GtkWidget *bt _U_, gpointer data)
1847 user_data_t *user_data = data;
1849 if (user_data->dlg.dialog_graph.window != NULL) {
1850 /* There's already a graph window; reactivate it. */
1851 reactivate_window(user_data->dlg.dialog_graph.window);
1855 dialog_graph_init_window(user_data);
1859 /****************************************************************************/
1860 static void on_goto_bt_clicked(GtkWidget *bt _U_, gpointer data)
1862 user_data_t *user_data = data;
1864 GtkTreeModel *model;
1865 GtkTreeSelection *selection;
1868 selection = user_data->dlg.selected_list_sel;
1870 if (selection==NULL)
1873 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1874 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1875 cf_goto_frame(&cfile, fnumber);
1880 static void draw_stat(user_data_t *user_data);
1882 /****************************************************************************/
1883 /* re-dissects all packets */
1884 static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data)
1886 user_data_t *user_data = data;
1887 GString *error_string;
1889 /* remove tap listener */
1890 remove_tap_listener(user_data);
1892 /* register tap listener */
1893 error_string = register_tap_listener("IAX2", user_data, NULL, 0,
1894 iax2_reset, iax2_packet, iax2_draw);
1895 if (error_string != NULL) {
1896 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1897 g_string_free(error_string, TRUE);
1901 /* retap all packets */
1902 cf_retap_packets(&cfile);
1904 /* draw statistics info */
1905 draw_stat(user_data);
1909 /****************************************************************************/
1910 static void on_next_bt_clicked(GtkWidget *bt _U_, gpointer data)
1912 user_data_t *user_data = data;
1914 GtkTreeModel *model;
1916 GtkTreeSelection *selection;
1919 selection = user_data->dlg.selected_list_sel;
1921 if (selection==NULL)
1925 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1926 while (gtk_tree_model_iter_next (model,&iter)) {
1927 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1928 if (strcmp(text, OK_TEXT) != 0) {
1929 gtk_tree_selection_select_iter (selection, &iter);
1930 path = gtk_tree_model_get_path(model, &iter);
1931 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1934 gtk_tree_path_free(path);
1941 if (user_data->dlg.number_of_nok>1){
1942 /* Get the first iter and select it before starting over */
1943 gtk_tree_model_get_iter_first(model, &iter);
1944 gtk_tree_selection_select_iter (selection, &iter);
1950 /****************************************************************************/
1951 /* when we want to save the information */
1952 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1955 GtkWidget *rev, *forw, *both;
1956 user_data_t *user_data;
1958 GtkListStore *store;
1960 GtkTreeModel *model;
1961 gboolean more_items = TRUE;
1963 /* To Hold data from the list row */
1964 guint packet; /* Packet */
1965 gfloat delta; /* Delta(ms) */
1966 gfloat jitter; /* Jitter(ms) */
1967 gfloat ipbw; /* IP BW(kbps) */
1968 char *status_str; /* Status */
1969 char *date_str; /* Date */
1970 guint length; /* Length */
1975 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1977 /* Perhaps the user specified a directory instead of a file.
1978 * Check whether they did.
1980 if (test_for_directory(g_dest) == EISDIR) {
1981 /* It's a directory - set the file selection box to display it. */
1982 set_last_open_dir(g_dest);
1983 file_selection_set_current_folder(fc, get_last_open_dir());
1984 gtk_file_chooser_set_current_name(fc, "");
1986 return FALSE; /* run the dialog again */
1988 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1989 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1990 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1991 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1993 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
1994 fp = ws_fopen(g_dest, "w");
1996 open_failure_alert_box(g_dest, errno, TRUE);
1998 return TRUE; /* we're done */
2001 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2002 fprintf(fp, "Forward\n");
2004 write_failure_alert_box(g_dest, errno);
2007 return TRUE; /* we're done */
2011 for(j = 0; j < NUM_COLS; j++) {
2013 fprintf(fp,"\"%s\"",titles[j]);
2015 fprintf(fp,",\"%s\"",titles[j]);
2020 write_failure_alert_box(g_dest, errno);
2023 return TRUE; /* we're done */
2025 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2026 store = GTK_LIST_STORE(model);
2027 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2029 while (more_items) {
2030 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2039 fprintf(fp, "\"%u\"",packet);
2040 fprintf(fp, ",\"%.2f\"", delta);
2041 fprintf(fp, ",\"%.2f\"", jitter);
2042 fprintf(fp, ",\"%.2f\"", ipbw);
2043 fprintf(fp, ",\"%s\"", status_str);
2044 fprintf(fp, ",\"%s\"", date_str);
2045 fprintf(fp, ",\"%u\"", length);
2050 write_failure_alert_box(g_dest, errno);
2053 return TRUE; /* we're done */
2056 more_items = gtk_tree_model_iter_next (model,&iter);
2060 if (fclose(fp) == EOF) {
2061 write_failure_alert_box(g_dest, errno);
2063 return TRUE; /* we're done */
2067 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2069 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2070 fp = ws_fopen(g_dest, "a");
2072 open_failure_alert_box(g_dest, errno, TRUE);
2074 return TRUE; /* we're done */
2076 fprintf(fp, "\nReverse\n");
2078 write_failure_alert_box(g_dest, errno);
2081 return TRUE; /* we're done */
2084 fp = ws_fopen(g_dest, "w");
2086 open_failure_alert_box(g_dest, errno, TRUE);
2088 return TRUE; /* we're done */
2091 for(j = 0; j < NUM_COLS; j++) {
2093 fprintf(fp,"\"%s\"",titles[j]);
2095 fprintf(fp,",\"%s\"",titles[j]);
2100 write_failure_alert_box(g_dest, errno);
2103 return TRUE; /* we're done */
2105 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2106 store = GTK_LIST_STORE(model);
2107 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2111 while (more_items) {
2112 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2121 fprintf(fp, "\"%u\"",packet);
2122 fprintf(fp, ",\"%.2f\"", delta);
2123 fprintf(fp, ",\"%.2f\"", jitter);
2124 fprintf(fp, ",\"%.2f\"", ipbw);
2125 fprintf(fp, ",\"%s\"", status_str);
2126 fprintf(fp, ",\"%s\"", date_str);
2127 fprintf(fp, ",\"%u\"", length);
2132 write_failure_alert_box(g_dest, errno);
2135 return TRUE; /* we're done */
2138 more_items = gtk_tree_model_iter_next (model,&iter);
2141 if (fclose(fp) == EOF) {
2142 write_failure_alert_box(g_dest, errno);
2144 return TRUE; /* we're done */
2148 return TRUE; /* we're done */
2151 static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2153 user_data_t *user_data = data;
2155 user_data->dlg.save_csv_as_w = NULL;
2158 /* when the user wants to save the csv information in a file */
2159 static void save_csv_as_cb(GtkWidget *bt _U_, gpointer data)
2161 user_data_t *user_data = data;
2164 GtkWidget *label_format;
2165 GtkWidget *channels_label;
2166 GtkWidget *forward_rb;
2167 GtkWidget *reversed_rb;
2170 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2171 if (user_data->dlg.save_csv_as_w != NULL) {
2172 /* There's already a Save CSV info dialog box; reactivate it. */
2173 reactivate_window(user_data->dlg.save_csv_as_w);
2177 user_data->dlg.save_csv_as_w =
2178 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2179 GTK_WINDOW(user_data->dlg.notebook),
2180 GTK_FILE_CHOOSER_ACTION_SAVE,
2181 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2182 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2184 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2185 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2187 /* Build our "extra widget" to be added to the file chooser widget */
2188 /* Container for each row of widgets */
2189 vertb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2190 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2191 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2192 gtk_widget_show (vertb);
2194 table1 = gtk_table_new (2, 4, FALSE);
2195 gtk_widget_show (table1);
2196 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2197 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2198 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2200 label_format = gtk_label_new ("Format: Comma Separated Values");
2201 gtk_widget_show (label_format);
2202 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2203 (GtkAttachOptions) (GTK_FILL),
2204 (GtkAttachOptions) (0), 0, 0);
2205 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2207 channels_label = gtk_label_new ("Channels: ");
2208 gtk_widget_show (channels_label);
2209 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2210 (GtkAttachOptions) (GTK_FILL),
2211 (GtkAttachOptions) (0), 0, 0);
2212 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2214 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2215 gtk_widget_show (forward_rb);
2216 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2217 (GtkAttachOptions) (GTK_FILL),
2218 (GtkAttachOptions) (0), 0, 0);
2220 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2221 gtk_widget_show (reversed_rb);
2222 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2223 (GtkAttachOptions) (GTK_FILL),
2224 (GtkAttachOptions) (0), 0, 0);
2226 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2227 gtk_widget_show (both_rb);
2228 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2229 (GtkAttachOptions) (GTK_FILL),
2230 (GtkAttachOptions) (0), 0, 0);
2232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2234 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2235 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2236 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2237 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2239 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2240 G_CALLBACK(window_delete_event_cb), NULL);
2241 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2242 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2244 gtk_widget_show(user_data->dlg.save_csv_as_w);
2245 window_present(user_data->dlg.save_csv_as_w);
2247 /* "Run" the GtkFileChooserDialog. */
2248 /* Upon exit: If "Accept" run the OK callback. */
2249 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2250 /* Destroy the window. */
2251 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2252 /* return with a TRUE status so that the dialog window will be destroyed. */
2253 /* Trying to re-run the dialog after popping up an alert box will not work */
2254 /* since the user will not be able to dismiss the alert box. */
2255 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2256 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2258 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2259 /* GtkFileChooserDialog. */
2260 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2261 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2262 break; /* we're done */
2265 window_destroy(user_data->dlg.save_csv_as_w);
2268 /****************************************************************************/
2269 static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer data)
2271 user_data_t *user_data = data;
2273 /* Note that we no longer have a Save voice info dialog box. */
2274 user_data->dlg.save_voice_as_w = NULL;
2277 /****************************************************************************/
2278 /* here we save it into a file that user specified */
2279 /* XXX what about endians here? could go something wrong? */
2280 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2282 int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
2283 gchar f_pd[1] = {0};
2284 gchar r_pd[1] = {0};
2287 guint32 f_write_silence = 0;
2288 guint32 r_write_silence = 0;
2290 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2291 gboolean stop_flag = FALSE;
2294 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2297 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2303 /* open file for saving */
2304 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2311 progbar = create_progress_dlg(top_level, "Saving voice in a file", dest, TRUE, &stop_flag);
2313 if (format == SAVE_AU_FORMAT) /* au format */
2315 /* First we write the .au header. XXX Hope this is endian independant */
2316 /* the magic word 0x2e736e64 == .snd */
2317 /* XXX: Should we be checking for write errors below ? */
2318 phtonl(pd, 0x2e736e64);
2319 fwritten = ws_write(to_fd, pd, 4);
2320 if ((fwritten < 4) || (fread_cnt < 0)) {
2324 destroy_progress_dlg(progbar);
2327 /* header offset == 24 bytes */
2329 fwritten = ws_write(to_fd, pd, 4);
2330 if ((fwritten < 4) || (fread_cnt < 0)) {
2334 destroy_progress_dlg(progbar);
2337 /* total length, it is permited to set this to 0xffffffff */
2339 fwritten = ws_write(to_fd, pd, 4);
2340 if ((fwritten < 4) || (fread_cnt < 0)) {
2344 destroy_progress_dlg(progbar);
2347 /* encoding format == 16-bit linear PCM */
2349 fwritten = ws_write(to_fd, pd, 4);
2350 if ((fwritten < 4) || (fread_cnt < 0)) {
2354 destroy_progress_dlg(progbar);
2357 /* sample rate == 8000 Hz */
2359 fwritten = ws_write(to_fd, pd, 4);
2360 if ((fwritten < 4) || (fread_cnt < 0)) {
2364 destroy_progress_dlg(progbar);
2369 fwritten = ws_write(to_fd, pd, 4);
2370 if ((fwritten < 4) || (fread_cnt < 0)) {
2374 destroy_progress_dlg(progbar);
2379 /* only forward direction */
2380 case SAVE_FORWARD_DIRECTION_MASK: {
2381 progbar_count = user_data->forward.saveinfo.count;
2382 progbar_quantum = user_data->forward.saveinfo.count/100;
2383 while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
2386 if((count > progbar_nextstep) && (count <= progbar_count)) {
2387 update_progress_dlg(progbar,
2388 (gfloat) count/progbar_count, "Saving");
2389 progbar_nextstep = progbar_nextstep + progbar_quantum;
2393 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2394 sample = ulaw2linear(*f_pd);
2397 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2398 sample = alaw2linear(*f_pd);
2405 destroy_progress_dlg(progbar);
2409 fwritten = ws_write(to_fd, pd, 2);
2410 if ((fwritten < 2) || (fread_cnt < 0)) {
2414 destroy_progress_dlg(progbar);
2420 /* only reversed direction */
2421 case SAVE_REVERSE_DIRECTION_MASK: {
2422 progbar_count = user_data->reversed.saveinfo.count;
2423 progbar_quantum = user_data->reversed.saveinfo.count/100;
2424 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2427 if((count > progbar_nextstep) && (count <= progbar_count)) {
2428 update_progress_dlg(progbar,
2429 (gfloat) count/progbar_count, "Saving");
2430 progbar_nextstep = progbar_nextstep + progbar_quantum;
2434 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2435 sample = ulaw2linear(*r_pd);
2438 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2439 sample = alaw2linear(*r_pd);
2446 destroy_progress_dlg(progbar);
2450 rwritten = ws_write(to_fd, pd, 2);
2451 if ((rwritten < 2) || (rread < 0)) {
2455 destroy_progress_dlg(progbar);
2461 /* both directions */
2462 case SAVE_BOTH_DIRECTION_MASK: {
2463 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2464 (progbar_count = user_data->forward.saveinfo.count) :
2465 (progbar_count = user_data->reversed.saveinfo.count);
2466 progbar_quantum = progbar_count/100;
2467 /* since conversation in one way can start later than in the other one,
2468 * we have to write some silence information for one channel */
2469 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2470 f_write_silence = (guint32)
2471 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2473 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2474 r_write_silence = (guint32)
2475 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2480 if((count > progbar_nextstep) && (count <= progbar_count)) {
2481 update_progress_dlg(progbar,
2482 (gfloat) count/progbar_count, "Saving");
2483 progbar_nextstep = progbar_nextstep + progbar_quantum;
2486 if(f_write_silence > 0) {
2487 rread = read(rev_fd, r_pd, 1);
2488 switch (user_data->forward.statinfo.reg_pt) {
2489 case AST_FORMAT_ULAW:
2490 *f_pd = SILENCE_PCMU;
2492 case AST_FORMAT_ALAW:
2493 *f_pd = SILENCE_PCMA;
2499 else if(r_write_silence > 0) {
2500 fread_cnt = read(forw_fd, f_pd, 1);
2501 switch (user_data->reversed.statinfo.reg_pt) {
2502 case AST_FORMAT_ULAW:
2503 *r_pd = SILENCE_PCMU;
2505 case AST_FORMAT_ALAW:
2506 *r_pd = SILENCE_PCMA;
2513 fread_cnt = read(forw_fd, f_pd, 1);
2514 rread = read(rev_fd, r_pd, 1);
2516 if ((rread == 0) && (fread_cnt == 0))
2518 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2519 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2522 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2523 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2531 destroy_progress_dlg(progbar);
2536 rwritten = ws_write(to_fd, pd, 2);
2537 if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
2541 destroy_progress_dlg(progbar);
2548 else if (format == SAVE_RAW_FORMAT) /* raw format */
2552 /* only forward direction */
2553 case SAVE_FORWARD_DIRECTION_MASK: {
2554 progbar_count = user_data->forward.saveinfo.count;
2555 progbar_quantum = user_data->forward.saveinfo.count/100;
2559 /* only reversed direction */
2560 case SAVE_REVERSE_DIRECTION_MASK: {
2561 progbar_count = user_data->reversed.saveinfo.count;
2562 progbar_quantum = user_data->reversed.saveinfo.count/100;
2570 destroy_progress_dlg(progbar);
2577 /* XXX how do you just copy the file? */
2578 while ((rread = read(fd, pd, 1)) > 0) {
2581 if((count > progbar_nextstep) && (count <= progbar_count)) {
2582 update_progress_dlg(progbar,
2583 (gfloat) count/progbar_count, "Saving");
2584 progbar_nextstep = progbar_nextstep + progbar_quantum;
2588 rwritten = ws_write(to_fd, pd, 1);
2590 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2594 destroy_progress_dlg(progbar);
2600 destroy_progress_dlg(progbar);
2608 /****************************************************************************/
2609 /* the user wants to save in a file */
2610 /* XXX support for different formats is currently commented out */
2611 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2614 /*GtkWidget *wav, *sw;*/
2615 GtkWidget *au, *raw;
2616 GtkWidget *rev, *forw, *both;
2617 user_data_t *user_data;
2618 gint channels , format;
2620 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2622 /* Perhaps the user specified a directory instead of a file.
2623 Check whether they did. */
2624 if (test_for_directory(g_dest) == EISDIR) {
2625 /* It's a directory - set the file selection box to display it. */
2626 set_last_open_dir(g_dest);
2627 file_selection_set_current_folder(fc, get_last_open_dir());
2628 gtk_file_chooser_set_current_name(fc, "");
2630 return FALSE; /* run the dialog again */
2634 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2635 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2637 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2638 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2639 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2640 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2641 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2642 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2644 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2645 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2646 * disable the ok button or disable the buttons for direction if only one is not ok. The
2647 * problem is if we open the save voice dialog and then click the refresh button and maybe
2648 * the state changes, so we can't save anymore. In this case we should be able to update
2649 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2652 /* we can not save in both directions */
2653 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2654 /* there are many combinations here, we just exit when first matches */
2655 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2656 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2657 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2658 "Can't save in a file: Unsupported codec!");
2659 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2660 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2661 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2662 "Can't save in a file: Wrong length of captured packets!");
2663 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2664 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2665 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2666 "Can't save in a file: Not all data in all packets was captured!");
2668 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2669 "Can't save in a file: File I/O problem!");
2671 return TRUE; /* we're done */
2673 /* we can not save forward direction */
2674 else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
2675 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2676 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2677 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2678 "Can't save forward direction in a file: Unsupported codec!");
2679 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2680 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2681 "Can't save forward direction in a file: Wrong length of captured packets!");
2682 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2683 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2684 "Can't save forward direction in a file: Not all data in all packets was captured!");
2686 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2687 "Can't save forward direction in a file: File I/O problem!");
2689 return TRUE; /* we're done */
2691 /* we can not save reversed direction */
2692 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
2693 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2694 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2695 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2696 "Can't save reversed direction in a file: Unsupported codec!");
2697 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2698 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2699 "Can't save reversed direction in a file: Wrong length of captured packets!");
2700 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2701 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2702 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2703 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2704 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2705 "Can't save reversed direction in a file: No IAX2 data!");
2707 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2708 "Can't save reversed direction in a file: File I/O problem!");
2710 return TRUE; /* we're done */
2714 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2715 format = SAVE_WAV_FORMAT;
2718 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2719 format = SAVE_AU_FORMAT;
2721 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2722 format = SAVE_SW_FORMAT;
2724 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2725 format = SAVE_RAW_FORMAT;
2727 format = SAVE_NONE_FORMAT;
2729 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2730 channels = SAVE_REVERSE_DIRECTION_MASK;
2731 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2732 channels = SAVE_BOTH_DIRECTION_MASK;
2734 channels = SAVE_FORWARD_DIRECTION_MASK;
2736 /* direction/format validity*/
2737 if (format == SAVE_AU_FORMAT)
2739 /* make sure streams are alaw/ulaw */
2740 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2741 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2742 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2744 return TRUE; /* we're done */
2746 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2747 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2748 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2750 return TRUE; /* we're done */
2752 /* make sure pt's don't differ */
2753 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2754 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2755 "Can't save in a file: Forward and reverse direction differ in type");
2757 return TRUE; /* we're done */
2760 else if (format == SAVE_RAW_FORMAT)
2762 /* can't save raw in both directions */
2763 if (channels == SAVE_BOTH_DIRECTION_MASK){
2764 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2765 "Can't save in a file: Unable to save raw data in both directions");
2767 return TRUE; /* we're done */
2772 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2773 "Can't save in a file: Invalid save format");
2775 return TRUE; /* we're done */
2778 if(!copy_file(g_dest, channels, format, user_data)) {
2779 /* XXX - report the error type! */
2780 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2781 "An error occurred while saving voice in a file!");
2783 return TRUE; /* we're done */
2786 return TRUE; /* we're done */
2789 /****************************************************************************/
2790 /* when the user wants to save the voice information in a file */
2791 /* XXX support for different formats is currently commented out */
2792 static void save_voice_as_cb(GtkWidget *bt _U_, gpointer data)
2794 user_data_t *user_data = data;
2797 GtkWidget *label_format;
2798 GtkWidget *channels_label;
2799 GtkWidget *forward_rb;
2800 GtkWidget *reversed_rb;
2802 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2806 /* if we can't save in a file: wrong codec, cut packets or other errors */
2807 /* should the error arise here or later when you click ok button ?
2808 * if we do it here, then we must disable the refresh button, so we don't do it here */
2810 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2811 if (user_data->dlg.save_voice_as_w != NULL) {
2812 /* There's already a Save voice info dialog box; reactivate it. */
2813 reactivate_window(user_data->dlg.save_voice_as_w);
2817 /* XXX - use file_selection from dlg_utils instead! */
2818 user_data->dlg.save_voice_as_w =
2819 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2820 GTK_WINDOW(user_data->dlg.notebook),
2821 GTK_FILE_CHOOSER_ACTION_SAVE,
2822 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2823 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2825 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2826 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2828 /* Container for each row of widgets */
2829 vertb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2830 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2831 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2832 gtk_widget_show (vertb);
2834 table1 = gtk_table_new (2, 4, FALSE);
2835 gtk_widget_show (table1);
2836 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2837 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2838 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2841 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2842 gtk_widget_show (label_format);
2843 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2844 (GtkAttachOptions) (GTK_FILL),
2845 (GtkAttachOptions) (0), 0, 0);
2848 label_format = gtk_label_new ("Format: ");
2849 gtk_widget_show (label_format);
2850 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2851 (GtkAttachOptions) (GTK_FILL),
2852 (GtkAttachOptions) (0), 0, 0);
2854 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2856 raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2857 gtk_widget_show (raw_rb);
2858 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2859 (GtkAttachOptions) (GTK_FILL),
2860 (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 /* we support .au - ulaw*/
2871 wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
2872 gtk_widget_show (wav_rb);
2873 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2874 (GtkAttachOptions) (GTK_FILL),
2875 (GtkAttachOptions) (0), 0, 0);
2877 sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
2878 gtk_widget_show (sw_rb);
2879 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2880 (GtkAttachOptions) (GTK_FILL),
2881 (GtkAttachOptions) (0), 0, 0);
2882 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2883 gtk_widget_show (au_rb);
2884 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2885 (GtkAttachOptions) (GTK_FILL),
2886 (GtkAttachOptions) (0), 0, 0);
2889 channels_label = gtk_label_new ("Channels: ");
2890 gtk_widget_show (channels_label);
2891 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2892 (GtkAttachOptions) (GTK_FILL),
2893 (GtkAttachOptions) (0), 0, 0);
2894 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2896 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2897 gtk_widget_show (forward_rb);
2898 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2899 (GtkAttachOptions) (GTK_FILL),
2900 (GtkAttachOptions) (0), 0, 0);
2902 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2903 gtk_widget_show (reversed_rb);
2904 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2905 (GtkAttachOptions) (GTK_FILL),
2906 (GtkAttachOptions) (0), 0, 0);
2908 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2909 gtk_widget_show (both_rb);
2910 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2911 (GtkAttachOptions) (GTK_FILL),
2912 (GtkAttachOptions) (0), 0, 0);
2914 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2917 /* if one direction is nok we don't allow saving
2918 XXX this is not ok since the user can click the refresh button and cause changes
2919 but we can not update this window. So we move all the decision on the time the ok
2920 button is clicked */
2921 if (user_data->forward.saved == FALSE) {
2922 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2923 gtk_widget_set_sensitive(forward_rb, FALSE);
2924 gtk_widget_set_sensitive(both_rb, FALSE);
2926 else if (user_data->reversed.saved == FALSE) {
2927 gtk_widget_set_sensitive(reversed_rb, FALSE);
2928 gtk_widget_set_sensitive(both_rb, FALSE);
2932 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2933 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2934 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2935 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2936 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2937 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2938 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2939 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2941 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2942 G_CALLBACK(window_delete_event_cb), NULL);
2943 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2944 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2946 gtk_widget_show(user_data->dlg.save_voice_as_w);
2947 window_present(user_data->dlg.save_voice_as_w);
2949 /* "Run" the GtkFileChooserDialog. */
2950 /* Upon exit: If "Accept" run the OK callback. */
2951 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2952 /* Destroy the window. */
2953 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2954 /* return with a TRUE status so that the dialog window will be destroyed. */
2955 /* Trying to re-run the dialog after popping up an alert box will not work */
2956 /* since the user will not be able to dismiss the alert box. */
2957 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2958 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2960 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2961 /* GtkFileChooserDialog. */
2962 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2963 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2964 break; /* we're done */
2967 window_destroy(user_data->dlg.save_voice_as_w);
2971 /****************************************************************************/
2972 /* when we are finished with redisection, we add the label for the statistic */
2973 static void draw_stat(user_data_t *user_data)
2975 gchar label_max[200];
2977 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2978 user_data->forward.statinfo.total_nr,
2979 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2981 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2983 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2984 user_data->reversed.statinfo.total_nr,
2985 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2987 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2994 /****************************************************************************/
2995 /* append a line to list */
2996 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2997 double delta, double jitter, double bandwidth, gchar *status,
2998 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
3000 GtkListStore *list_store;
3002 if (strcmp(status, OK_TEXT) != 0) {
3003 user_data->dlg.number_of_nok++;
3006 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
3008 /* Creates a new row at position. iter will be changed to point to this new row.
3009 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
3010 * The row will be filled with the values given to this function.
3012 * should generally be preferred when inserting rows in a sorted list store.
3014 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3015 PACKET_COLUMN, number,
3016 DELTA_COLUMN, delta,
3017 JITTER_COLUMN, jitter,
3018 IPBW_COLUMN, bandwidth,
3019 STATUS_COLUMN, (char *)status,
3020 DATE_COLUMN, (char *)timeStr,
3021 LENGTH_COLUMN, pkt_len,
3022 FOREGROUND_COLOR_COL, NULL,
3023 BACKGROUND_COLOR_COL, (char *)color_str,
3026 if(flags & STAT_FLAG_FIRST){
3027 /* Set first row as active */
3028 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3032 /****************************************************************************
3033 * Functions needed to present values from the list
3036 /* Present floats with two decimals */
3038 iax2_float_data_func (GtkTreeViewColumn *column _U_,
3039 GtkCellRenderer *renderer,
3040 GtkTreeModel *model,
3048 /* the col to get data from is in userdata */
3049 gint float_col = GPOINTER_TO_INT(user_data);
3051 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
3053 /* save the current locale */
3054 savelocale = setlocale(LC_NUMERIC, NULL);
3055 /* switch to "C" locale to avoid problems with localized decimal separators
3056 * in g_snprintf("%f") functions
3058 setlocale(LC_NUMERIC, "C");
3060 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
3061 /* restore previous locale setting */
3062 setlocale(LC_NUMERIC, savelocale);
3064 g_object_set(renderer, "text", buf, NULL);
3070 GtkWidget* create_list(user_data_t* user_data)
3073 GtkListStore *list_store;
3075 GtkTreeViewColumn *column;
3076 GtkCellRenderer *renderer;
3077 GtkTreeSortable *sortable;
3078 GtkTreeView *list_view;
3079 GtkTreeSelection *selection;
3081 /* Create the store */
3082 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
3083 G_TYPE_UINT, /* Packet */
3084 G_TYPE_FLOAT, /* Delta(ms) */
3085 G_TYPE_FLOAT, /* Jitter(ms) */
3086 G_TYPE_FLOAT, /* IP BW(kbps) */
3087 G_TYPE_STRING, /* Status */
3088 G_TYPE_STRING, /* Date */
3089 G_TYPE_UINT, /* Length */
3090 G_TYPE_STRING, /* Foreground color */
3091 G_TYPE_STRING); /* Background color */
3094 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3096 list_view = GTK_TREE_VIEW(list);
3097 sortable = GTK_TREE_SORTABLE(list_store);
3099 /* Speed up the list display */
3100 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3102 /* Setup the sortable columns */
3103 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3104 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3106 /* The view now holds a reference. We can get rid of our own reference */
3107 g_object_unref (G_OBJECT (list_store));
3110 * Create the first column packet, associating the "text" attribute of the
3111 * cell_renderer to the first column of the model
3113 renderer = gtk_cell_renderer_text_new ();
3114 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3115 "text", PACKET_COLUMN,
3116 "foreground", FOREGROUND_COLOR_COL,
3117 "background", BACKGROUND_COLOR_COL,
3119 gtk_tree_view_column_set_sort_column_id(column, PACKET_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);
3124 /* Add the column to the view. */
3125 gtk_tree_view_append_column (list_view, column);
3127 /* Second column.. Delta(ms). */
3128 renderer = gtk_cell_renderer_text_new ();
3129 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3130 "text", DELTA_COLUMN,
3131 "foreground", FOREGROUND_COLOR_COL,
3132 "background", BACKGROUND_COLOR_COL,
3135 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3136 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3138 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3139 gtk_tree_view_column_set_resizable(column, TRUE);
3140 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3141 gtk_tree_view_column_set_min_width(column, 100);
3142 gtk_tree_view_append_column (list_view, column);
3144 /* Third column.. Jitter(ms). */
3145 renderer = gtk_cell_renderer_text_new ();
3146 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3147 "text", JITTER_COLUMN,
3148 "foreground", FOREGROUND_COLOR_COL,
3149 "background", BACKGROUND_COLOR_COL,
3152 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3153 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3155 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3156 gtk_tree_view_column_set_resizable(column, TRUE);
3157 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3158 gtk_tree_view_column_set_min_width(column, 100);
3159 gtk_tree_view_append_column (list_view, column);
3161 /* Fourth column.. IP BW(kbps). */
3162 renderer = gtk_cell_renderer_text_new ();
3163 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3164 "text", IPBW_COLUMN,
3165 "foreground", FOREGROUND_COLOR_COL,
3166 "background", BACKGROUND_COLOR_COL,
3169 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3170 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3172 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3173 gtk_tree_view_column_set_resizable(column, TRUE);
3174 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3175 gtk_tree_view_column_set_min_width(column, 100);
3176 gtk_tree_view_append_column (list_view, column);
3178 /* Fifth column.. Status. */
3179 renderer = gtk_cell_renderer_text_new ();
3180 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3181 "text", STATUS_COLUMN,
3182 "foreground", FOREGROUND_COLOR_COL,
3183 "background", BACKGROUND_COLOR_COL,
3185 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3186 gtk_tree_view_column_set_resizable(column, TRUE);
3187 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3188 gtk_tree_view_column_set_min_width(column, 100);
3189 gtk_tree_view_append_column (list_view, column);
3191 /* Sixth column.. Length. */
3192 renderer = gtk_cell_renderer_text_new ();
3193 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3194 "text", LENGTH_COLUMN,
3195 "foreground", FOREGROUND_COLOR_COL,
3196 "background", BACKGROUND_COLOR_COL,
3200 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3201 gtk_tree_view_column_set_resizable(column, TRUE);
3202 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3203 gtk_tree_view_column_set_min_width(column, 100);
3204 gtk_tree_view_append_column (list_view, column);
3206 /* Now enable the sorting of each column */
3207 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3208 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3210 /* Setup the selection handler */
3211 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3212 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3214 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3215 G_CALLBACK (on_list_select_row),
3222 /****************************************************************************/
3223 /* Create the dialog box with all widgets */
3224 static void create_iax2_dialog(user_data_t* user_data)
3226 GtkWidget *window = NULL;
3227 GtkWidget *list_fwd;
3228 GtkWidget *list_rev;
3229 GtkWidget *label_stats_fwd;
3230 GtkWidget *label_stats_rev;
3231 GtkWidget *notebook;
3233 GtkWidget *main_vb, *page, *page_r;
3235 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3236 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3237 GtkWidget *graph_bt;
3238 gchar label_forward[150];
3239 gchar label_reverse[150];
3241 gchar str_ip_src[16];
3242 gchar str_ip_dst[16];
3244 window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
3245 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3247 /* Container for each row of widgets */
3248 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 2, FALSE);
3249 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3250 gtk_container_add(GTK_CONTAINER(window), main_vb);
3251 gtk_widget_show(main_vb);
3254 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3255 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3257 g_snprintf(label_forward, sizeof(label_forward),
3258 "Analysing stream from %s port %u to %s port %u ",
3259 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3262 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3263 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3265 g_snprintf(label_reverse, sizeof(label_reverse),
3266 "Analysing stream from %s port %u to %s port %u ",
3267 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3269 /* Start a notebook for flipping between sets of changes */
3270 notebook = gtk_notebook_new();
3271 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3272 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3274 user_data->dlg.notebook_signal_id =
3275 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3277 /* page for forward connection */
3278 page = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3279 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3281 /* direction label */
3282 label = gtk_label_new(label_forward);
3283 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3285 /* place for some statistics */
3286 label_stats_fwd = gtk_label_new("\n");
3287 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3289 /* scrolled window */
3290 scrolled_window = scrolled_window_new(NULL, NULL);
3293 list_fwd = create_list(user_data);
3294 gtk_widget_show(list_fwd);
3295 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3296 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3297 gtk_widget_show(scrolled_window);
3300 label = gtk_label_new(" Forward Direction ");
3301 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3303 /* same page for reversed connection */
3304 page_r = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3305 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3306 label = gtk_label_new(label_reverse);
3307 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3308 label_stats_rev = gtk_label_new("\n");
3309 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3311 scrolled_window_r = scrolled_window_new(NULL, NULL);
3313 list_rev = create_list(user_data);
3314 gtk_widget_show(list_rev);
3315 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3316 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3317 gtk_widget_show(scrolled_window_r);
3319 label = gtk_label_new(" Reversed Direction ");
3320 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3323 /* page for help&about or future */
3324 page_help = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
3325 label = gtk_label_new(" Future ");
3326 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3327 frame = gtk_frame_new("");
3328 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3329 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3330 gtk_container_add(GTK_CONTAINER(frame), text);
3331 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3332 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3335 /* show all notebooks */
3336 gtk_widget_show_all(notebook);
3339 box4 = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3340 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3341 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3342 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3343 gtk_box_set_spacing(GTK_BOX (box4), 0);
3344 gtk_widget_show(box4);
3346 voice_bt = gtk_button_new_with_label("Save payload...");
3347 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3348 gtk_widget_show(voice_bt);
3349 g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3351 csv_bt = gtk_button_new_with_label("Save as CSV...");
3352 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3353 gtk_widget_show(csv_bt);
3354 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3356 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3357 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3358 gtk_widget_show(refresh_bt);
3359 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3361 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3362 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3363 gtk_widget_show(goto_bt);
3364 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3366 graph_bt = gtk_button_new_with_label("Graph");
3367 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3368 gtk_widget_show(graph_bt);
3369 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3371 next_bt = gtk_button_new_with_label("Next non-Ok");
3372 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3373 gtk_widget_show(next_bt);
3374 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3376 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3377 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3378 gtk_widget_set_can_default(close_bt, TRUE);
3379 gtk_widget_show(close_bt);
3380 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3382 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3383 g_signal_connect(window, "destroy", G_CALLBACK(on_iax2_window_destroy), user_data);
3385 gtk_widget_show(window);
3386 window_present(window);
3388 /* some widget references need to be saved for outside use */
3389 user_data->dlg.window = window;
3390 user_data->dlg.list_fwd = list_fwd;
3391 user_data->dlg.list_rev = list_rev;
3392 user_data->dlg.label_stats_fwd = label_stats_fwd;
3393 user_data->dlg.label_stats_rev = label_stats_rev;
3394 user_data->dlg.notebook = notebook;
3395 user_data->dlg.selected_list = list_fwd;
3396 user_data->dlg.number_of_nok = 0;
3399 * select the initial row
3401 gtk_widget_grab_focus(list_fwd);
3405 /****************************************************************************/
3406 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3407 const gchar* proto_field, guint32* p_result)
3410 proto_node *proto_sibling_node;
3411 header_field_info *hfssrc;
3414 finfo = PNODE_FINFO(ptree_node);
3416 if (hfinformation==(finfo->hfinfo)) {
3417 hfssrc = proto_registrar_get_byname(proto_field);
3418 if (hfssrc == NULL) {
3419 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3423 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3424 ptree_node=ptree_node->next) {
3425 finfo=PNODE_FINFO(ptree_node);
3426 if (hfssrc==finfo->hfinfo) {
3427 if (hfinformation->type==FT_IPv4) {
3428 ipv4 = fvalue_get(&finfo->value);
3429 *p_result = ipv4_get_net_order_addr(ipv4);
3432 *p_result = fvalue_get_uinteger(&finfo->value);
3441 proto_sibling_node = ptree_node->next;
3443 if (proto_sibling_node) {
3444 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3450 /****************************************************************************/
3451 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3452 const gchar* proto_name,
3453 const gchar* proto_field,
3456 proto_node *ptree_node;
3457 header_field_info *hfinformation;
3459 hfinformation = proto_registrar_get_byname(proto_name);
3460 if (hfinformation == NULL) {
3461 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3466 ptree_node = ((proto_node *)protocol_tree)->first_child;
3468 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3472 return process_node(ptree_node, hfinformation, proto_field, p_result);
3476 /****************************************************************************/
3478 address *ip_src_fwd,
3479 guint16 port_src_fwd,
3480 address *ip_dst_fwd,
3481 guint16 port_dst_fwd,
3482 address *ip_src_rev,
3483 guint16 port_src_rev,
3484 address *ip_dst_rev,
3485 guint16 port_dst_rev
3488 user_data_t *user_data;
3491 static GdkColor col[MAX_GRAPHS] = {
3492 {0, 0x0000, 0x0000, 0x0000},
3493 {0, 0xffff, 0x0000, 0x0000},
3494 {0, 0x0000, 0xffff, 0x0000},
3495 {0, 0x0000, 0x0000, 0xffff}
3497 #if GTK_CHECK_VERSION(3,0,0)
3498 static GdkRGBA rgba_col[MAX_GRAPHS] = {
3499 {0.0, 0.0, 0.0, 1.0}, /* Black */
3500 {1.0, 0.0, 0.1, 1.0}, /* Red */
3501 {0.0, 1.0, 0.0, 1.0}, /* Green */
3502 {0.0, 0.0, 1.0, 1.0}, /* Blue */
3509 user_data = g_malloc(sizeof(user_data_t));
3511 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3512 user_data->port_src_fwd = port_src_fwd;
3513 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3514 user_data->port_dst_fwd = port_dst_fwd;
3515 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3516 user_data->port_src_rev = port_src_rev;
3517 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3518 user_data->port_dst_rev = port_dst_rev;
3521 /* file names for storing sound data */
3522 fd = create_tempfile(&tempname, "wireshark_iax2_f");
3524 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3525 "Can't create temporary file for IAX2 analysis:\n%s.",
3530 user_data->f_tempname = g_strdup(tempname);
3532 fd = create_tempfile(&tempname, "wireshark_iax2_r");
3534 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3535 "Can't create temporary file for IAX2 analysis:\n%s.",
3537 g_free(user_data->f_tempname);
3541 user_data->r_tempname = g_strdup(tempname);
3543 user_data->forward.saveinfo.fp = NULL;
3544 user_data->reversed.saveinfo.fp = NULL;
3545 user_data->dlg.save_voice_as_w = NULL;
3546 user_data->dlg.save_csv_as_w = NULL;
3547 user_data->dlg.dialog_graph.window = NULL;
3549 /* init dialog_graph */
3550 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3551 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
3552 user_data->dlg.dialog_graph.draw_area=NULL;
3553 #if GTK_CHECK_VERSION(2,22,0)
3554 user_data->dlg.dialog_graph.surface=NULL;
3556 user_data->dlg.dialog_graph.pixmap=NULL;
3558 user_data->dlg.dialog_graph.scrollbar=NULL;
3559 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3560 user_data->dlg.dialog_graph.surface_width=500;
3561 user_data->dlg.dialog_graph.surface_height=200;
3562 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3563 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3564 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3565 user_data->dlg.dialog_graph.max_interval=0;
3566 user_data->dlg.dialog_graph.num_items=0;
3567 user_data->dlg.dialog_graph.start_time = -1;
3569 for(i=0;i<MAX_GRAPHS;i++){
3570 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3571 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3572 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3573 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3574 #if GTK_CHECK_VERSION(3,0,0)
3575 user_data->dlg.dialog_graph.graph[i].rgba_color.red=rgba_col[i].red;
3576 user_data->dlg.dialog_graph.graph[i].rgba_color.green=rgba_col[i].green;
3577 user_data->dlg.dialog_graph.graph[i].rgba_color.blue=rgba_col[i].blue;
3578 user_data->dlg.dialog_graph.graph[i].rgba_color.alpha=rgba_col[i].alpha;
3580 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3581 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3582 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3585 /* create the dialog box */
3586 create_iax2_dialog(user_data);
3588 /* proceed as if the Refresh button would have been pressed */
3589 on_refresh_bt_clicked(NULL, user_data);
3592 /****************************************************************************/
3593 /* entry point from main menu */
3594 void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3597 guint16 port_src_fwd;
3599 guint16 port_dst_fwd;
3601 guint16 port_src_rev;
3603 guint16 port_dst_rev;
3604 /* unsigned int ptype; */
3606 gchar filter_text[256];
3610 gboolean frame_matched;
3612 GList *strinfo_list;
3613 GList *filtered_list = NULL;
3614 rtp_stream_info_t *strinfo;
3617 /* Try to compile the filter. */
3618 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3619 if (!dfilter_compile(filter_text, &sfcode)) {
3620 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3623 /* we load the current file into cf variable */
3625 fdata = cf->current_frame;
3627 /* we are on the selected frame now */
3629 return; /* if we exit here it's an error */
3631 /* dissect the current frame */
3632 if (!cf_read_frame(cf, fdata))
3633 return; /* error reading the frame */
3634 epan_dissect_init(&edt, TRUE, FALSE);
3635 epan_dissect_prime_dfilter(&edt, sfcode);
3636 epan_dissect_run(&edt, &cf->phdr, cf->pd, fdata, NULL);
3638 /* if it is not an iax2 frame, show an error dialog */
3639 frame_matched = dfilter_apply_edt(sfcode, &edt);
3640 if (frame_matched != 1) {
3641 epan_dissect_cleanup(&edt);
3642 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3643 "You didn't choose a IAX2 packet!");
3646 /* check if it is Voice or MiniPacket
3647 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3648 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3649 "Please select a Voice packet!");
3653 /* check if it is part of a Call */
3654 if (edt.pi.circuit_id == 0) {
3655 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3656 "Please select a Call packet!");
3660 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3661 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3662 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3663 port_src_fwd = edt.pi.srcport;
3664 port_dst_fwd = edt.pi.destport;
3666 /* assume the inverse ip/port combination for the reverse direction */
3667 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3668 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3669 port_src_rev = edt.pi.destport;
3670 port_dst_rev = edt.pi.srcport;
3672 /* Scan for rtpstream */
3674 /* search for reversed direction in the global rtp streams list */
3676 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3677 while (strinfo_list)
3679 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3680 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3681 && strinfo->src_port==port_src_fwd
3682 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3683 && strinfo->dest_port==port_dst_fwd)
3685 filtered_list = g_list_prepend(filtered_list, strinfo);
3688 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3689 && strinfo->src_port==port_src_rev
3690 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3691 && strinfo->dest_port==port_dst_rev)
3694 filtered_list = g_list_append(filtered_list, strinfo);
3697 strinfo_list = g_list_next(strinfo_list);
3700 /* if more than one reverse streams found, we let the user choose the right one */
3702 rtpstream_dlg_show(filtered_list);
3719 /****************************************************************************/
3721 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3723 iax2_analysis_cb(NULL, NULL);
3726 /****************************************************************************/
3728 register_tap_listener_iax2_analysis(void)
3730 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3740 * indent-tabs-mode: t
3743 * ex: set shiftwidth=8 tabstop=8 noexpandtab:
3744 * :indentSize=8:tabSize=8:noTabs=false: