2 * IAX2 analysis addition for Wireshark
6 * based on rtp_analysis.c
7 * Copyright 2003, Alcatel Business Systems
8 * By Lars Ruoff <lars.ruoff@gmx.net>
11 * Copyright 2003, Iskratel, Ltd, Kranj
12 * By Miha Jemec <m.jemec@iskratel.si>
14 * Graph. Copyright 2004, Verso Technology
15 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
16 * Based on io_stat.c by Ronnie Sahlberg
18 * Wireshark - Network traffic analyzer
19 * By Gerald Combs <gerald@wireshark.org>
20 * Copyright 1998 Gerald Combs
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License
24 * as published by the Free Software Foundation; either version 2
25 * of the License, or (at your option) any later version.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
56 #include <epan/epan_dissect.h>
57 #include <epan/filesystem.h>
58 #include <epan/pint.h>
60 #include <epan/tap-voip.h>
61 #include <epan/dissectors/packet-iax2.h>
62 #include <epan/iax2_codec_type.h>
63 #include <epan/addr_resolv.h>
64 #include <epan/stat_cmd_args.h>
65 #include <epan/strutil.h>
68 #include "../register.h"
70 #include "../alert_box.h"
71 #include "../simple_dialog.h"
72 #include "../stat_menu.h"
73 #include "../progress_dlg.h"
75 #include "../tempfile.h"
76 #include <wsutil/file_util.h>
78 #include "gtk/gtkglobals.h"
79 #include "gtk/dlg_utils.h"
80 #include "gtk/file_dlg.h"
81 #include "gtk/gui_utils.h"
82 #include "gtk/gui_stat_menu.h"
84 #include "gtk/rtp_analysis.h"
85 #include "gtk/iax2_analysis.h"
86 #include "gtk/rtp_stream.h"
87 #include "gtk/rtp_stream_dlg.h"
100 N_COLUMN /* The number of columns */
103 /****************************************************************************/
105 typedef struct column_arrows {
107 GtkWidget *ascend_pm;
108 GtkWidget *descend_pm;
112 #define NUM_GRAPH_ITEMS 100000
113 #define MAX_YSCALE 16
114 #define AUTO_MAX_YSCALE 0
116 #define GRAPH_FWD_JITTER 0
117 #define GRAPH_FWD_DIFF 1
118 #define GRAPH_REV_JITTER 2
119 #define GRAPH_REV_DIFF 3
120 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
122 #define MAX_PIXELS_PER_TICK 4
123 #define DEFAULT_PIXELS_PER_TICK 1
124 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
125 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
127 #define MAX_TICK_VALUES 5
128 #define DEFAULT_TICK_VALUE 1
129 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
130 typedef struct _dialog_graph_graph_item_t {
133 } dialog_graph_graph_item_t;
135 typedef struct _dialog_graph_graph_t {
136 struct _user_data_t *ud;
137 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
140 GtkWidget *display_button;
145 } dialog_graph_graph_t;
148 typedef struct _dialog_graph_t {
149 gboolean needs_redraw;
150 gint32 interval; /* measurement interval in ms */
151 guint32 last_interval;
152 guint32 max_interval; /* XXX max_interval and num_items are redundant */
154 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
156 GtkWidget *draw_area;
158 GtkAdjustment *scrollbar_adjustment;
159 GtkWidget *scrollbar;
167 typedef struct _dialog_data_t {
172 GtkWidget *label_stats_fwd;
173 GtkWidget *label_stats_rev;
174 GtkWidget *selected_list;
176 GtkTreeSelection *selected_list_sel;
177 gint selected_list_row;
179 GtkWidget *save_voice_as_w;
180 GtkWidget *save_csv_as_w;
181 gint notebook_signal_id;
182 dialog_graph_t dialog_graph;
185 #define OK_TEXT "[ Ok ]"
187 /* type of error when saving voice in a file didn't succeed */
190 TAP_RTP_WRONG_LENGTH,
191 TAP_RTP_PADDING_ERROR,
193 TAP_RTP_FILE_OPEN_ERROR,
197 typedef struct _tap_iax2_save_info_t {
200 error_type_t error_type;
202 } tap_iax2_save_info_t;
205 /* structure that holds the information about the forward and reversed direction */
206 struct _info_direction {
207 tap_iax2_stat_t statinfo;
208 tap_iax2_save_info_t saveinfo;
211 #define SILENCE_PCMU (guint8)0xFF
212 #define SILENCE_PCMA (guint8)0x55
214 /* structure that holds general information about the connection
215 * and structures for both directions */
216 typedef struct _user_data_t {
217 /* tap associated data*/
219 guint16 port_src_fwd;
221 guint16 port_dst_fwd;
223 guint16 port_src_rev;
225 guint16 port_dst_rev;
227 struct _info_direction forward;
228 struct _info_direction reversed;
233 /* dialog associated data */
240 static const gchar *titles[7] = {
250 #define SAVE_FORWARD_DIRECTION_MASK 0x01
251 #define SAVE_REVERSE_DIRECTION_MASK 0x02
252 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
254 #define SAVE_NONE_FORMAT 0
255 #define SAVE_WAV_FORMAT 1
256 #define SAVE_AU_FORMAT 2
257 #define SAVE_SW_FORMAT 3
258 #define SAVE_RAW_FORMAT 4
261 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
262 /****************************************************************************/
263 static void enable_graph(dialog_graph_graph_t *dgg)
270 static void dialog_graph_reset(user_data_t* user_data);
274 /****************************************************************************/
277 /****************************************************************************/
278 /* when there is a [re]reading of packet's */
280 iax2_reset(void *user_data_arg)
282 user_data_t *user_data = user_data_arg;
283 user_data->forward.statinfo.first_packet = TRUE;
284 user_data->reversed.statinfo.first_packet = TRUE;
285 user_data->forward.statinfo.max_delta = 0;
286 user_data->reversed.statinfo.max_delta = 0;
287 user_data->forward.statinfo.max_jitter = 0;
288 user_data->reversed.statinfo.max_jitter = 0;
289 user_data->forward.statinfo.mean_jitter = 0;
290 user_data->reversed.statinfo.mean_jitter = 0;
291 user_data->forward.statinfo.delta = 0;
292 user_data->reversed.statinfo.delta = 0;
293 user_data->forward.statinfo.diff = 0;
294 user_data->reversed.statinfo.diff = 0;
295 user_data->forward.statinfo.jitter = 0;
296 user_data->reversed.statinfo.jitter = 0;
297 user_data->forward.statinfo.bandwidth = 0;
298 user_data->reversed.statinfo.bandwidth = 0;
299 user_data->forward.statinfo.total_bytes = 0;
300 user_data->reversed.statinfo.total_bytes = 0;
301 user_data->forward.statinfo.bw_start_index = 0;
302 user_data->reversed.statinfo.bw_start_index = 0;
303 user_data->forward.statinfo.bw_index = 0;
304 user_data->reversed.statinfo.bw_index = 0;
305 user_data->forward.statinfo.timestamp = 0;
306 user_data->reversed.statinfo.timestamp = 0;
307 user_data->forward.statinfo.max_nr = 0;
308 user_data->reversed.statinfo.max_nr = 0;
309 user_data->forward.statinfo.total_nr = 0;
310 user_data->reversed.statinfo.total_nr = 0;
311 user_data->forward.statinfo.sequence = 0;
312 user_data->reversed.statinfo.sequence = 0;
313 user_data->forward.statinfo.start_seq_nr = 0;
314 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
315 user_data->forward.statinfo.stop_seq_nr = 0;
316 user_data->reversed.statinfo.stop_seq_nr = 0;
317 user_data->forward.statinfo.cycles = 0;
318 user_data->reversed.statinfo.cycles = 0;
319 user_data->forward.statinfo.under = FALSE;
320 user_data->reversed.statinfo.under = FALSE;
321 user_data->forward.statinfo.start_time = 0;
322 user_data->reversed.statinfo.start_time = 0;
323 user_data->forward.statinfo.time = 0;
324 user_data->reversed.statinfo.time = 0;
325 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
326 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
328 user_data->forward.saveinfo.count = 0;
329 user_data->reversed.saveinfo.count = 0;
330 user_data->forward.saveinfo.saved = FALSE;
331 user_data->reversed.saveinfo.saved = FALSE;
333 /* clear the dialog box lists */
334 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
335 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
337 /* reset graph info */
338 dialog_graph_reset(user_data);
341 /* XXX check for error at fclose? */
342 if (user_data->forward.saveinfo.fp != NULL)
343 fclose(user_data->forward.saveinfo.fp);
344 if (user_data->reversed.saveinfo.fp != NULL)
345 fclose(user_data->reversed.saveinfo.fp);
346 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
347 if (user_data->forward.saveinfo.fp == NULL)
348 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
349 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
350 if (user_data->reversed.saveinfo.fp == NULL)
351 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
355 /****************************************************************************/
356 static int iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
358 dialog_graph_graph_item_t *it;
362 /* we sometimes get called when dgg is disabled.
363 this is a bug since the tap listener should be removed first */
368 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
371 * Find which interval this is supposed to to in and store the
372 * interval index as idx
374 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
375 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
377 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
381 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
383 /* some sanity checks */
384 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
388 /* update num_items */
389 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
390 dgg->ud->dlg.dialog_graph.num_items=idx;
391 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
395 * Find the appropriate dialog_graph_graph_item_t structure
400 * Use the max value to highlight RTP problems
402 if (value > it->value) {
405 it->flags = it->flags | statinfo->flags;
410 /****************************************************************************/
411 /* here we can redraw the output */
413 static void iax2_draw(void *prs _U_)
418 /* forward declarations */
419 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
420 double delta, double jitter, double bandwidth, gchar *status,
421 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
423 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
424 tap_iax2_stat_t *statinfo, packet_info *pinfo,
425 const struct _iax2_info_t *iax2info);
427 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
428 tap_iax2_stat_t *statinfo,
430 const struct _iax2_info_t *iax2info);
433 /****************************************************************************/
434 /* whenever a IAX2 packet is seen by the tap listener */
435 static int iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
437 user_data_t *user_data = user_data_arg;
438 const struct _iax2_info_t *iax2info = iax2info_arg;
440 /* we ignore packets that are not displayed */
441 if (pinfo->fd->flags.passed_dfilter == 0)
444 /* we ignore packets that carry no data */
445 if (iax2info->payload_len == 0)
448 /* is it the forward direction? */
449 else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
450 && user_data->port_src_fwd == pinfo->srcport
451 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
452 && user_data->port_dst_fwd == pinfo->destport) {
453 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
454 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.jitter*1000000));
455 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.diff*1000000));
456 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
457 &(user_data->forward.statinfo), pinfo, iax2info);
458 iax2_packet_save_payload(&(user_data->forward.saveinfo),
459 &(user_data->forward.statinfo), pinfo, iax2info);
461 /* is it the reversed direction? */
462 else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
463 && user_data->port_src_rev == pinfo->srcport
464 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
465 && user_data->port_dst_rev == pinfo->destport) {
466 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
467 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
468 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
469 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
470 &(user_data->reversed.statinfo), pinfo, iax2info);
471 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
472 &(user_data->reversed.statinfo), pinfo, iax2info);
478 /****************************************************************************/
479 /* This comes from tap-rtp-common.c */
480 /****************************************************************************/
482 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
484 const struct _iax2_info_t *iax2info)
487 double current_jitter;
491 /* check payload type */
492 if (iax2info->ftype == AST_FRAME_VOICE) {
493 if (iax2info->csub != statinfo->pt)
494 statinfo->flags |= STAT_FLAG_PT_CHANGE;
495 statinfo->pt = iax2info->csub;
498 /* store the current time and calculate the current jitter */
499 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
500 current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
501 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
502 statinfo->delta = current_time-(statinfo->time);
503 statinfo->jitter = current_jitter;
504 statinfo->diff = current_diff;
506 /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
507 statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
508 statinfo->bw_history[statinfo->bw_index].time = current_time;
509 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
510 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
511 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
512 statinfo->bw_start_index++;
513 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
515 statinfo->total_bytes += iax2info->payload_len + 24;
516 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
517 statinfo->bw_index++;
518 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
521 /* is this the first packet we got in this direction? */
522 if (statinfo->first_packet) {
523 statinfo->start_seq_nr = 0;
524 statinfo->start_time = current_time;
526 statinfo->jitter = 0;
528 statinfo->flags |= STAT_FLAG_FIRST;
529 statinfo->first_packet = FALSE;
531 /* is it a regular packet? */
532 if (!(statinfo->flags & STAT_FLAG_FIRST)
533 && !(statinfo->flags & STAT_FLAG_MARKER)
534 && !(statinfo->flags & STAT_FLAG_PT_CN)
535 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
536 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
537 /* include it in maximum delta calculation */
538 if (statinfo->delta > statinfo->max_delta) {
539 statinfo->max_delta = statinfo->delta;
540 statinfo->max_nr = pinfo->fd->num;
542 /* maximum and mean jitter calculation */
543 if (statinfo->jitter > statinfo->max_jitter) {
544 statinfo->max_jitter = statinfo->jitter;
546 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
548 /* regular payload change? (CN ignored) */
549 if (!(statinfo->flags & STAT_FLAG_FIRST)
550 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
551 if ((statinfo->pt != statinfo->reg_pt)
552 && (statinfo->reg_pt != PT_UNDEFINED)) {
553 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
557 /* set regular payload*/
558 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
559 statinfo->reg_pt = statinfo->pt;
562 /* TODO: lost packets / duplicated: we should infer this from timestamp... */
563 statinfo->time = current_time;
564 statinfo->timestamp = iax2info->timestamp;
565 statinfo->stop_seq_nr = 0;
566 statinfo->total_nr++;
572 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
573 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
574 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
575 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
576 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
578 /****************************************************************************/
579 /* adds statistics information from the packet to the list */
580 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
581 tap_iax2_stat_t *statinfo, packet_info *pinfo,
582 const struct _iax2_info_t *iax2info _U_)
589 GdkColor color = COLOR_DEFAULT;
591 then = pinfo->fd->abs_ts.secs;
592 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
593 tm_tmp = localtime(&then);
594 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
597 tm_tmp->tm_year + 1900,
603 /* Default to using black on white text if nothing below overrides it */
604 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
606 if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
607 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
609 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
611 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
612 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
613 color = COLOR_WARNING;
614 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
616 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
617 g_snprintf(status,sizeof(status),"Incorrect timestamp");
618 color = COLOR_WARNING;
619 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
621 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
622 && !(statinfo->flags & STAT_FLAG_FIRST)
623 && !(statinfo->flags & STAT_FLAG_PT_CN)
624 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
625 && !(statinfo->flags & STAT_FLAG_MARKER)) {
626 g_snprintf(status,sizeof(status),"Marker missing?");
627 color = COLOR_WARNING;
628 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
631 if (statinfo->flags & STAT_FLAG_MARKER) {
632 color = COLOR_WARNING;
633 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
635 g_snprintf(status,sizeof(status),OK_TEXT);
637 /* is this the first packet we got in this direction? */
638 if (statinfo->flags & STAT_FLAG_FIRST) {
639 add_to_list(list, user_data,
645 timeStr, pinfo->fd->pkt_len,
650 add_to_list(list, user_data,
652 statinfo->delta*1000,
653 statinfo->jitter*1000,
656 timeStr, pinfo->fd->pkt_len,
663 #define MAX_SILENCE_TICKS 1000000
664 /****************************************************************************/
665 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
666 tap_iax2_stat_t *statinfo,
668 const struct _iax2_info_t *iax2info)
673 /* is this the first packet we got in this direction? */
674 if (statinfo->flags & STAT_FLAG_FIRST) {
675 if (saveinfo->fp == NULL) {
676 saveinfo->saved = FALSE;
677 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
680 saveinfo->saved = TRUE;
683 /* save the voice information */
684 /* if there was already an error, we quit */
685 if (saveinfo->saved == FALSE)
688 /* if the captured length and packet length aren't equal, we quit */
689 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
690 saveinfo->saved = FALSE;
691 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
695 if (iax2info->payload_len > 0) {
696 data = iax2info->payload_data;
697 nchars=fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
698 saveinfo->count+=iax2info->payload_len;
700 fflush(saveinfo->fp);
701 saveinfo->saved = TRUE;
709 /****************************************************************************/
712 /****************************************************************************/
714 /****************************************************************************/
715 /* close the dialog window and remove the tap listener */
716 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
718 /* remove tap listener */
719 protect_thread_critical_region();
720 remove_tap_listener(user_data);
721 unprotect_thread_critical_region();
723 /* close and remove temporary files */
724 if (user_data->forward.saveinfo.fp != NULL)
725 fclose(user_data->forward.saveinfo.fp);
726 if (user_data->reversed.saveinfo.fp != NULL)
727 fclose(user_data->reversed.saveinfo.fp);
728 /*XXX: test for error **/
729 ws_remove(user_data->f_tempname);
730 ws_remove(user_data->r_tempname);
732 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
733 /* destroy save_csv_as window if open */
734 if (user_data->dlg.save_csv_as_w != NULL)
735 window_destroy(user_data->dlg.save_csv_as_w);
737 /* destroy save_voice_as window if open */
738 if (user_data->dlg.save_voice_as_w != NULL)
739 window_destroy(user_data->dlg.save_voice_as_w);
741 /* destroy graph window if open */
742 if (user_data->dlg.dialog_graph.window != NULL)
743 window_destroy(user_data->dlg.dialog_graph.window);
746 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
747 /* XXX: Is this still true for GTK2 ??? */
748 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
750 g_free(user_data->f_tempname);
751 g_free(user_data->r_tempname);
756 /****************************************************************************/
757 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
758 GtkNotebookPage *page _U_,
760 user_data_t *user_data _U_)
762 user_data->dlg.selected_list =
763 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
765 user_data->dlg.selected_list_row = 0;
768 /****************************************************************************/
769 static void on_list_select_row(GtkTreeSelection *selection,
770 user_data_t *user_data _U_/*gpointer data */)
772 user_data->dlg.selected_list_sel = selection;
776 /****************************************************************************/
777 static void dialog_graph_set_title(user_data_t* user_data)
780 if (!user_data->dlg.dialog_graph.window){
783 title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
784 get_addr_name(&(user_data->ip_src_fwd)),
785 user_data->port_src_fwd,
786 get_addr_name(&(user_data->ip_dst_fwd)),
787 user_data->port_dst_fwd,
788 get_addr_name(&(user_data->ip_src_rev)),
789 user_data->port_src_rev,
790 get_addr_name(&(user_data->ip_dst_rev)),
791 user_data->port_dst_rev);
793 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
799 /****************************************************************************/
800 static void dialog_graph_reset(user_data_t* user_data)
804 user_data->dlg.dialog_graph.needs_redraw=TRUE;
805 for(i=0;i<MAX_GRAPHS;i++){
806 for(j=0;j<NUM_GRAPH_ITEMS;j++){
807 dialog_graph_graph_item_t *dggi;
808 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
813 user_data->dlg.dialog_graph.last_interval=0xffffffff;
814 user_data->dlg.dialog_graph.max_interval=0;
815 user_data->dlg.dialog_graph.num_items=0;
817 /* create the color titles near the filter buttons */
818 for(i=0;i<MAX_GRAPHS;i++){
821 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
822 sizeof (user_data->dlg.dialog_graph.graph[0].title),
823 "%s: %s:%u to %s:%u",
825 get_addr_name(&(user_data->ip_src_fwd)),
826 user_data->port_src_fwd,
827 get_addr_name(&(user_data->ip_dst_fwd)),
828 user_data->port_dst_fwd);
831 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
832 sizeof(user_data->dlg.dialog_graph.graph[0].title),
833 "%s: %s:%u to %s:%u",
835 get_addr_name(&(user_data->ip_src_rev)),
836 user_data->port_src_rev,
837 get_addr_name(&(user_data->ip_dst_rev)),
838 user_data->port_dst_rev);
842 dialog_graph_set_title(user_data);
845 /****************************************************************************/
846 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
848 dialog_graph_graph_item_t *it;
855 /****************************************************************************/
856 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
859 g_snprintf(buf, buf_len, "%ds",t/1000000);
860 } else if(t>=1000000){
861 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
863 g_snprintf(buf, buf_len, "%dms",t/1000);
865 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
867 g_snprintf(buf, buf_len, "%dus",t);
871 /****************************************************************************/
872 static void dialog_graph_draw(user_data_t* user_data)
875 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
876 gint32 current_interval;
877 guint32 left_x_border;
878 guint32 right_x_border;
879 guint32 top_y_border;
880 guint32 bottom_y_border;
882 int label_width, label_height;
883 guint32 draw_width, draw_height;
884 char label_string[15];
887 guint32 num_time_intervals;
888 guint32 max_value; /* max value of seen data */
889 guint32 max_y; /* max value of the Y scale */
891 if(!user_data->dlg.dialog_graph.needs_redraw){
894 user_data->dlg.dialog_graph.needs_redraw=FALSE;
897 * Find the length of the intervals we have data for
898 * so we know how large arrays we need to malloc()
900 num_time_intervals=user_data->dlg.dialog_graph.num_items;
901 /* if there isnt anything to do, just return */
902 if(num_time_intervals==0){
905 num_time_intervals+=1;
906 /* XXX move this check to _packet() */
907 if(num_time_intervals>NUM_GRAPH_ITEMS){
908 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
913 * find the max value so we can autoscale the y axis
916 for(i=0;i<MAX_GRAPHS;i++){
919 if(!user_data->dlg.dialog_graph.graph[i].display){
922 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
925 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
927 /* keep track of the max value we have encountered */
937 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
938 user_data->dlg.dialog_graph.draw_area->style->white_gc,
941 user_data->dlg.dialog_graph.draw_area->allocation.width,
942 user_data->dlg.dialog_graph.draw_area->allocation.height);
946 * Calculate the y scale we should use
948 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
949 max_y=yscale_max[MAX_YSCALE-1];
950 for(i=MAX_YSCALE-1;i>0;i--){
951 if(max_value<yscale_max[i]){
956 /* the user had specified an explicit y scale to use */
957 max_y=user_data->dlg.dialog_graph.max_y_units;
961 * Calculate size of borders surrounding the plot
962 * The border on the right side needs to be adjusted depending
963 * on the width of the text labels. For simplicity we assume that the
964 * top y scale label will be the widest one
966 print_time_scale_string(label_string, sizeof(label_string), max_y);
967 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
968 pango_layout_get_pixel_size(layout, &label_width, &label_height);
970 right_x_border=label_width+20;
972 bottom_y_border=label_height+20;
976 * Calculate the size of the drawing area for the actual plot
978 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
979 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
983 * Draw the y axis and labels
984 * (we always draw the y scale with 11 ticks along the axis)
986 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
987 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
989 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
990 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
996 /* first, middle and last tick are slightly longer */
1000 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1001 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1002 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1003 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1004 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1005 /* draw the labels */
1007 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1008 pango_layout_set_text(layout, label_string, -1);
1009 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1010 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1011 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1012 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1013 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1017 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1018 pango_layout_set_text(layout, label_string, -1);
1019 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1020 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1021 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1022 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1023 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1027 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1028 pango_layout_set_text(layout, label_string, -1);
1029 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1030 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1031 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1032 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1033 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1041 * if we have not specified the last_interval via the gui,
1042 * then just pick the current end of the capture so that is scrolls
1043 * nicely when doing live captures
1045 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1046 last_interval=user_data->dlg.dialog_graph.max_interval;
1048 last_interval=user_data->dlg.dialog_graph.last_interval;
1055 /* plot the x-scale */
1056 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc, left_x_border, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1, user_data->dlg.dialog_graph.pixmap_width-right_x_border+1, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1);
1058 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1059 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1060 first_interval*=user_data->dlg.dialog_graph.interval;
1067 while(interval_delta<((last_interval-first_interval)/10)){
1068 interval_delta*=delta_multiplier;
1069 if(delta_multiplier==5){
1076 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1079 /* if pixels_per_tick is <5, only draw every 10 ticks */
1080 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1084 if(current_interval%interval_delta){
1090 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1091 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1092 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1093 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1094 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1095 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1098 if(user_data->dlg.dialog_graph.interval>=1000){
1099 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1100 } else if(user_data->dlg.dialog_graph.interval>=100){
1101 g_snprintf(label_string, sizeof(label_string),
1102 "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1103 } else if(user_data->dlg.dialog_graph.interval>=10){
1104 g_snprintf(label_string, sizeof(label_string),
1105 "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1107 g_snprintf(label_string, sizeof(label_string),
1108 "%d.%3ds", current_interval/1000,current_interval%1000);
1110 pango_layout_set_text(layout, label_string, -1);
1111 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1112 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1113 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1114 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1115 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1127 * Draw "x" for Sequence Errors and "m" for Marks
1129 /* Draw the labels Fwd and Rev */
1130 g_strlcpy(label_string, "<-Fwd", sizeof(label_string));
1131 pango_layout_set_text(layout, label_string, -1);
1132 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1133 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1134 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1135 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1136 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1138 g_strlcpy(label_string, "<-Rev", sizeof(label_string));
1139 pango_layout_set_text(layout, label_string, -1);
1140 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1141 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1142 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1143 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1144 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1147 /* Draw the marks */
1148 for(i=MAX_GRAPHS-1;i>=0;i--){
1150 guint32 x_pos, prev_x_pos;
1152 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1153 if (!user_data->dlg.dialog_graph.graph[i].display){
1156 /* initialize prev x/y to the low left corner of the graph */
1157 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1159 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1160 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1162 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1163 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1164 g_strlcpy(label_string, "x", sizeof(label_string));
1166 g_strlcpy(label_string, "m", sizeof(label_string));
1169 pango_layout_set_text(layout, label_string, -1);
1170 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1171 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1172 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1174 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1182 g_object_unref(G_OBJECT(layout));
1185 * Loop over all graphs and draw them
1187 for(i=MAX_GRAPHS-1;i>=0;i--){
1189 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1190 if (!user_data->dlg.dialog_graph.graph[i].display){
1193 /* initialize prev x/y to the low left corner of the graph */
1194 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1195 prev_y_pos=draw_height-1+top_y_border;
1197 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1199 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1200 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1204 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1207 /* dont need to draw anything if the segment
1208 * is entirely above the top of the graph
1210 if( (prev_y_pos==0) && (y_pos==0) ){
1217 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1218 x_pos, draw_height-1+top_y_border,
1228 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1229 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1230 user_data->dlg.dialog_graph.pixmap,
1233 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1236 /* update the scrollbar */
1237 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1238 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1239 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1240 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1241 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1243 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1245 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1246 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1247 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1251 /****************************************************************************/
1252 static void dialog_graph_redraw(user_data_t* user_data)
1254 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1255 dialog_graph_draw(user_data);
1258 /****************************************************************************/
1260 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1262 user_data->dlg.dialog_graph.window = NULL;
1265 /****************************************************************************/
1266 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1268 user_data_t *user_data;
1270 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1272 exit(10); /* !! XXX: should be g_assert or something ? */
1276 gdk_draw_pixmap(widget->window,
1277 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1278 user_data->dlg.dialog_graph.pixmap,
1279 event->area.x, event->area.y,
1280 event->area.x, event->area.y,
1281 event->area.width, event->area.height);
1286 /****************************************************************************/
1287 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1289 user_data_t *user_data;
1292 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1298 if(user_data->dlg.dialog_graph.pixmap){
1299 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1300 user_data->dlg.dialog_graph.pixmap=NULL;
1303 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1304 widget->allocation.width,
1305 widget->allocation.height,
1307 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1308 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1310 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1311 widget->style->white_gc,
1314 widget->allocation.width,
1315 widget->allocation.height);
1317 /* set up the colors and the GC structs for this pixmap */
1318 for(i=0;i<MAX_GRAPHS;i++){
1319 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1320 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1323 dialog_graph_redraw(user_data);
1327 /****************************************************************************/
1328 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1330 user_data_t *user_data=(user_data_t *)data;
1333 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1334 if(user_data->dlg.dialog_graph.last_interval==mi){
1337 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1338 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1342 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1344 dialog_graph_redraw(user_data);
1348 /****************************************************************************/
1349 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1351 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1352 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1353 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1355 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1357 /* signals needed to handle backing pixmap */
1358 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1359 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1361 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1362 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1364 /* create the associated scrollbar */
1365 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1366 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1367 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1368 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1369 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1372 /****************************************************************************/
1373 static void disable_graph(dialog_graph_graph_t *dgg)
1377 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1382 /****************************************************************************/
1383 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1385 /* this graph is not active, just update display and redraw */
1386 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1388 dialog_graph_redraw(dgg->ud);
1393 cf_retap_packets(&cfile);
1394 dialog_graph_redraw(dgg->ud);
1399 /****************************************************************************/
1400 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1406 hbox=gtk_hbox_new(FALSE, 3);
1407 gtk_container_add(GTK_CONTAINER(box), hbox);
1408 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1409 gtk_widget_show(hbox);
1411 g_snprintf(str, sizeof(str), "Graph %d", num);
1412 dgg->display_button=gtk_toggle_button_new_with_label(str);
1413 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1414 gtk_widget_show(dgg->display_button);
1415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1416 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1418 label=gtk_label_new(dgg->title);
1419 gtk_widget_show(label);
1420 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1422 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1423 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1424 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1425 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1426 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1431 /****************************************************************************/
1432 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1439 frame=gtk_frame_new("Graphs");
1440 gtk_container_add(GTK_CONTAINER(box), frame);
1441 gtk_widget_show(frame);
1443 vbox=gtk_vbox_new(FALSE, 1);
1444 gtk_container_add(GTK_CONTAINER(frame), vbox);
1445 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1446 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1447 gtk_widget_show(vbox);
1449 for(i=0;i<MAX_GRAPHS;i++){
1450 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1453 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1454 gtk_widget_show(label);
1455 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1460 /****************************************************************************/
1461 static void yscale_select(GtkWidget *item, gpointer key)
1464 user_data_t *user_data;
1466 user_data=(user_data_t *)key;
1467 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1469 user_data->dlg.dialog_graph.max_y_units=yscale_max[i]/1000;
1470 dialog_graph_redraw(user_data);
1473 /****************************************************************************/
1474 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1477 user_data_t *user_data;
1479 user_data=(user_data_t *)key;
1480 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1481 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1482 dialog_graph_redraw(user_data);
1485 /****************************************************************************/
1486 static void tick_interval_select(GtkWidget *item, gpointer key)
1489 user_data_t *user_data;
1491 user_data=(user_data_t *)key;
1492 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1494 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1495 cf_retap_packets(&cfile);
1496 dialog_graph_redraw(user_data);
1499 /****************************************************************************/
1501 create_yscale_max_menu_items(user_data_t* user_data)
1504 GtkWidget *combo_box;
1507 combo_box = gtk_combo_box_new_text ();
1509 for(i=0;i<MAX_YSCALE;i++){
1510 if(yscale_max[i]==AUTO_MAX_YSCALE){
1511 g_strlcpy(str,"Auto",sizeof(str));
1513 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1515 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1517 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1518 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1523 /****************************************************************************/
1525 create_pixels_per_tick_menu_items(user_data_t *user_data)
1528 GtkWidget *combo_box;
1531 combo_box = gtk_combo_box_new_text ();
1533 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1534 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1535 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1537 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK);
1539 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1544 /****************************************************************************/
1546 create_tick_interval_menu_items(user_data_t *user_data)
1548 GtkWidget *combo_box;
1552 combo_box = gtk_combo_box_new_text ();
1554 for(i=0;i<MAX_TICK_VALUES;i++){
1555 if(tick_interval_values[i]>=1000){
1556 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1557 } else if(tick_interval_values[i]>=100){
1558 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1559 } else if(tick_interval_values[i]>=10){
1560 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1562 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1564 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1566 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1567 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1572 /****************************************************************************/
1573 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1577 GtkWidget *combo_box;
1579 hbox=gtk_hbox_new(FALSE, 0);
1580 gtk_container_add(GTK_CONTAINER(box), hbox);
1581 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1582 gtk_widget_show(hbox);
1584 label=gtk_label_new(name);
1585 gtk_widget_show(label);
1586 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1588 combo_box = (*func)(user_data);
1589 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1590 gtk_widget_show(combo_box);
1593 /****************************************************************************/
1594 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1596 GtkWidget *frame_vbox;
1600 frame_vbox=gtk_vbox_new(FALSE, 0);
1601 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1602 gtk_widget_show(frame_vbox);
1604 frame = gtk_frame_new("X Axis");
1605 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1606 gtk_widget_show(frame);
1608 vbox=gtk_vbox_new(FALSE, 0);
1609 gtk_container_add(GTK_CONTAINER(frame), vbox);
1610 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1611 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1612 gtk_widget_show(vbox);
1614 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1615 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1617 frame = gtk_frame_new("Y Axis");
1618 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1619 gtk_widget_show(frame);
1621 vbox=gtk_vbox_new(FALSE, 0);
1622 gtk_container_add(GTK_CONTAINER(frame), vbox);
1623 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1624 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1625 gtk_widget_show(vbox);
1627 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1632 /****************************************************************************/
1633 static void dialog_graph_init_window(user_data_t* user_data)
1637 GtkWidget *bt_close;
1639 /* create the main window */
1640 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1642 vbox=gtk_vbox_new(FALSE, 0);
1643 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1644 gtk_widget_show(vbox);
1646 create_draw_area(user_data, vbox);
1648 hbox=gtk_hbox_new(FALSE, 3);
1649 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1650 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1651 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1652 gtk_widget_show(hbox);
1654 create_filter_area(user_data, hbox);
1655 create_ctrl_area(user_data, hbox);
1657 dialog_graph_set_title(user_data);
1659 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1660 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1661 gtk_widget_show(hbox);
1663 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1664 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1666 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1668 gtk_widget_show(user_data->dlg.dialog_graph.window);
1669 window_present(user_data->dlg.dialog_graph.window);
1674 /****************************************************************************/
1675 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1677 if (user_data->dlg.dialog_graph.window != NULL) {
1678 /* There's already a graph window; reactivate it. */
1679 reactivate_window(user_data->dlg.dialog_graph.window);
1683 dialog_graph_init_window(user_data);
1687 /****************************************************************************/
1688 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1691 GtkTreeModel *model;
1692 GtkTreeSelection *selection;
1695 selection = user_data->dlg.selected_list_sel;
1697 if (selection==NULL)
1700 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1701 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1702 cf_goto_frame(&cfile, fnumber);
1707 static void draw_stat(user_data_t *user_data);
1709 /****************************************************************************/
1710 /* re-dissects all packets */
1711 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1713 GString *error_string;
1715 /* remove tap listener */
1716 protect_thread_critical_region();
1717 remove_tap_listener(user_data);
1718 unprotect_thread_critical_region();
1720 /* register tap listener */
1721 error_string = register_tap_listener("IAX2", user_data, NULL, 0,
1722 iax2_reset, iax2_packet, iax2_draw);
1723 if (error_string != NULL) {
1724 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1725 g_string_free(error_string, TRUE);
1729 /* retap all packets */
1730 cf_retap_packets(&cfile);
1732 /* draw statistics info */
1733 draw_stat(user_data);
1737 /****************************************************************************/
1738 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1741 GtkTreeModel *model;
1743 GtkTreeSelection *selection;
1746 selection = user_data->dlg.selected_list_sel;
1748 if (selection==NULL)
1752 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1753 while (gtk_tree_model_iter_next (model,&iter)) {
1754 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1755 if (strcmp(text, OK_TEXT) != 0) {
1756 gtk_tree_selection_select_iter (selection, &iter);
1757 path = gtk_tree_model_get_path(model, &iter);
1758 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1761 gtk_tree_path_free(path);
1768 if (user_data->dlg.number_of_nok>1){
1769 /* Get the first iter and select it before starting over */
1770 gtk_tree_model_get_iter_first(model, &iter);
1771 gtk_tree_selection_select_iter (selection, &iter);
1777 /****************************************************************************/
1778 /* when we want to save the information */
1779 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1782 GtkWidget *rev, *forw, *both;
1783 user_data_t *user_data;
1785 GtkListStore *store;
1787 GtkTreeModel *model;
1788 gboolean more_items = TRUE;
1790 /* To Hold data from the list row */
1791 guint packet; /* Packet */
1792 gfloat delta; /* Delta(ms) */
1793 gfloat jitter; /* Jitter(ms) */
1794 gfloat ipbw; /* IP BW(kbps) */
1795 char *status_str; /* Status */
1796 char *date_str; /* Date */
1797 guint length; /* Length */
1802 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1804 /* Perhaps the user specified a directory instead of a file.
1805 * Check whether they did.
1807 if (test_for_directory(g_dest) == EISDIR) {
1808 /* It's a directory - set the file selection box to display it. */
1809 set_last_open_dir(g_dest);
1810 file_selection_set_current_folder(fc, get_last_open_dir());
1811 gtk_file_chooser_set_current_name(fc, "");
1813 return FALSE; /* run the dialog again */
1815 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1816 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1817 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1818 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1820 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1821 fp = ws_fopen(g_dest, "w");
1823 open_failure_alert_box(g_dest, errno, TRUE);
1825 return TRUE; /* we're done */
1828 if (GTK_TOGGLE_BUTTON(both)->active) {
1829 fprintf(fp, "Forward\n");
1831 write_failure_alert_box(g_dest, errno);
1834 return TRUE; /* we're done */
1838 for(j = 0; j < NUM_COLS; j++) {
1840 fprintf(fp,"%s",titles[j]);
1842 fprintf(fp,",%s",titles[j]);
1847 write_failure_alert_box(g_dest, errno);
1850 return TRUE; /* we're done */
1852 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1853 store = GTK_LIST_STORE(model);
1854 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1856 while (more_items) {
1857 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1866 fprintf(fp, "%u",packet);
1867 fprintf(fp, ",%.2f", delta);
1868 fprintf(fp, ",%.2f", jitter);
1869 fprintf(fp, ",%.2f", ipbw);
1870 fprintf(fp, ",%s", status_str);
1871 fprintf(fp, ",%s", date_str);
1872 fprintf(fp, ",%u", length);
1877 write_failure_alert_box(g_dest, errno);
1880 return TRUE; /* we're done */
1883 more_items = gtk_tree_model_iter_next (model,&iter);
1887 if (fclose(fp) == EOF) {
1888 write_failure_alert_box(g_dest, errno);
1890 return TRUE; /* we're done */
1894 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1896 if (GTK_TOGGLE_BUTTON(both)->active) {
1897 fp = ws_fopen(g_dest, "a");
1899 open_failure_alert_box(g_dest, errno, TRUE);
1901 return TRUE; /* we're done */
1903 fprintf(fp, "\nReverse\n");
1905 write_failure_alert_box(g_dest, errno);
1908 return TRUE; /* we're done */
1911 fp = ws_fopen(g_dest, "w");
1913 open_failure_alert_box(g_dest, errno, TRUE);
1915 return TRUE; /* we're done */
1918 for(j = 0; j < NUM_COLS; j++) {
1920 fprintf(fp,"%s",titles[j]);
1922 fprintf(fp,",%s",titles[j]);
1927 write_failure_alert_box(g_dest, errno);
1930 return TRUE; /* we're done */
1932 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
1933 store = GTK_LIST_STORE(model);
1934 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1938 while (more_items) {
1939 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1948 fprintf(fp, "%u",packet);
1949 fprintf(fp, ",%.2f", delta);
1950 fprintf(fp, ",%.2f", jitter);
1951 fprintf(fp, ",%.2f", ipbw);
1952 fprintf(fp, ",%s", status_str);
1953 fprintf(fp, ",%s", date_str);
1954 fprintf(fp, ",%u", length);
1959 write_failure_alert_box(g_dest, errno);
1962 return TRUE; /* we're done */
1965 more_items = gtk_tree_model_iter_next (model,&iter);
1968 if (fclose(fp) == EOF) {
1969 write_failure_alert_box(g_dest, errno);
1971 return TRUE; /* we're done */
1975 return TRUE; /* we're done */
1978 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
1980 user_data->dlg.save_csv_as_w = NULL;
1983 /* when the user wants to save the csv information in a file */
1984 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
1988 GtkWidget *label_format;
1989 GtkWidget *channels_label;
1990 GSList *channels_group = NULL;
1991 GtkWidget *forward_rb;
1992 GtkWidget *reversed_rb;
1995 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
1996 if (user_data->dlg.save_csv_as_w != NULL) {
1997 /* There's already a Save CSV info dialog box; reactivate it. */
1998 reactivate_window(user_data->dlg.save_csv_as_w);
2002 user_data->dlg.save_csv_as_w =
2003 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2004 GTK_WINDOW(user_data->dlg.notebook),
2005 GTK_FILE_CHOOSER_ACTION_SAVE,
2006 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2007 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2009 #if GTK_CHECK_VERSION(2,8,0)
2010 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2012 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2014 /* Build our "extra widget" to be added to the file chooser widget */
2015 /* Container for each row of widgets */
2016 vertb = gtk_vbox_new(FALSE, 0);
2017 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2018 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2019 gtk_widget_show (vertb);
2021 table1 = gtk_table_new (2, 4, FALSE);
2022 gtk_widget_show (table1);
2023 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2024 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2025 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2027 label_format = gtk_label_new ("Format: Comma Separated Values");
2028 gtk_widget_show (label_format);
2029 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2030 (GtkAttachOptions) (GTK_FILL),
2031 (GtkAttachOptions) (0), 0, 0);
2032 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2034 channels_label = gtk_label_new ("Channels: ");
2035 gtk_widget_show (channels_label);
2036 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2037 (GtkAttachOptions) (GTK_FILL),
2038 (GtkAttachOptions) (0), 0, 0);
2039 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2041 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2042 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2043 gtk_widget_show (forward_rb);
2044 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2045 (GtkAttachOptions) (GTK_FILL),
2046 (GtkAttachOptions) (0), 0, 0);
2048 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2049 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2050 gtk_widget_show (reversed_rb);
2051 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2052 (GtkAttachOptions) (GTK_FILL),
2053 (GtkAttachOptions) (0), 0, 0);
2055 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2056 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2057 gtk_widget_show (both_rb);
2058 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2059 (GtkAttachOptions) (GTK_FILL),
2060 (GtkAttachOptions) (0), 0, 0);
2062 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2064 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2065 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2066 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2067 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2069 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2070 G_CALLBACK(window_delete_event_cb), NULL);
2071 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2072 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2074 gtk_widget_show(user_data->dlg.save_csv_as_w);
2075 window_present(user_data->dlg.save_csv_as_w);
2077 /* "Run" the GtkFileChooserDialog. */
2078 /* Upon exit: If "Accept" run the OK callback. */
2079 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2080 /* Destroy the window. */
2081 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2082 /* return with a TRUE status so that the dialog window will be destroyed. */
2083 /* Trying to re-run the dialog after popping up an alert box will not work */
2084 /* since the user will not be able to dismiss the alert box. */
2085 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2086 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2088 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2089 /* GtkFileChooserDialog. */
2090 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2091 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2092 break; /* we're done */
2095 window_destroy(user_data->dlg.save_csv_as_w);
2098 /****************************************************************************/
2099 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2101 /* Note that we no longer have a Save voice info dialog box. */
2102 user_data->dlg.save_voice_as_w = NULL;
2105 /****************************************************************************/
2106 /* here we save it into a file that user specified */
2107 /* XXX what about endians here? could go something wrong? */
2108 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2110 int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
2111 gchar f_pd[1] = {0};
2112 gchar r_pd[1] = {0};
2115 guint32 f_write_silence = 0;
2116 guint32 r_write_silence = 0;
2118 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2119 gboolean stop_flag = FALSE;
2122 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2125 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2131 /* open file for saving */
2132 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2139 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2141 if (format == SAVE_AU_FORMAT) /* au format */
2143 /* First we write the .au header. XXX Hope this is endian independant */
2144 /* the magic word 0x2e736e64 == .snd */
2145 phtonl(pd, 0x2e736e64);
2146 nchars=ws_write(to_fd, pd, 4);
2147 /* header offset == 24 bytes */
2149 nchars=ws_write(to_fd, pd, 4);
2150 /* total length, it is permited to set this to 0xffffffff */
2152 nchars=ws_write(to_fd, pd, 4);
2153 /* encoding format == 16-bit linear PCM */
2155 nchars=ws_write(to_fd, pd, 4);
2156 /* sample rate == 8000 Hz */
2158 nchars=ws_write(to_fd, pd, 4);
2161 nchars=ws_write(to_fd, pd, 4);
2165 /* only forward direction */
2166 case SAVE_FORWARD_DIRECTION_MASK: {
2167 progbar_count = user_data->forward.saveinfo.count;
2168 progbar_quantum = user_data->forward.saveinfo.count/100;
2169 while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
2172 if((count > progbar_nextstep) && (count <= progbar_count)) {
2173 update_progress_dlg(progbar,
2174 (gfloat) count/progbar_count, "Saving");
2175 progbar_nextstep = progbar_nextstep + progbar_quantum;
2179 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2180 sample = ulaw2linear(*f_pd);
2183 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2184 sample = alaw2linear(*f_pd);
2191 destroy_progress_dlg(progbar);
2195 fwritten = ws_write(to_fd, pd, 2);
2196 if ((fwritten < 2) || (fwritten < 0) || (fread_cnt < 0)) {
2200 destroy_progress_dlg(progbar);
2206 /* only reversed direction */
2207 case SAVE_REVERSE_DIRECTION_MASK: {
2208 progbar_count = user_data->reversed.saveinfo.count;
2209 progbar_quantum = user_data->reversed.saveinfo.count/100;
2210 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2213 if((count > progbar_nextstep) && (count <= progbar_count)) {
2214 update_progress_dlg(progbar,
2215 (gfloat) count/progbar_count, "Saving");
2216 progbar_nextstep = progbar_nextstep + progbar_quantum;
2220 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2221 sample = ulaw2linear(*r_pd);
2224 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2225 sample = alaw2linear(*r_pd);
2232 destroy_progress_dlg(progbar);
2236 rwritten = ws_write(to_fd, pd, 2);
2237 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2241 destroy_progress_dlg(progbar);
2247 /* both directions */
2248 case SAVE_BOTH_DIRECTION_MASK: {
2249 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2250 (progbar_count = user_data->forward.saveinfo.count) :
2251 (progbar_count = user_data->reversed.saveinfo.count);
2252 progbar_quantum = progbar_count/100;
2253 /* since conversation in one way can start later than in the other one,
2254 * we have to write some silence information for one channel */
2255 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2256 f_write_silence = (guint32)
2257 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2259 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2260 r_write_silence = (guint32)
2261 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2266 if((count > progbar_nextstep) && (count <= progbar_count)) {
2267 update_progress_dlg(progbar,
2268 (gfloat) count/progbar_count, "Saving");
2269 progbar_nextstep = progbar_nextstep + progbar_quantum;
2272 if(f_write_silence > 0) {
2273 rread = read(rev_fd, r_pd, 1);
2274 switch (user_data->forward.statinfo.reg_pt) {
2275 case AST_FORMAT_ULAW:
2276 *f_pd = SILENCE_PCMU;
2278 case AST_FORMAT_ALAW:
2279 *f_pd = SILENCE_PCMA;
2285 else if(r_write_silence > 0) {
2286 fread_cnt = read(forw_fd, f_pd, 1);
2287 switch (user_data->reversed.statinfo.reg_pt) {
2288 case AST_FORMAT_ULAW:
2289 *r_pd = SILENCE_PCMU;
2291 case AST_FORMAT_ALAW:
2292 *r_pd = SILENCE_PCMA;
2299 fread_cnt = read(forw_fd, f_pd, 1);
2300 rread = read(rev_fd, r_pd, 1);
2302 if ((rread == 0) && (fread_cnt == 0))
2304 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2305 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2308 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2309 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2317 destroy_progress_dlg(progbar);
2322 rwritten = ws_write(to_fd, pd, 2);
2323 if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
2327 destroy_progress_dlg(progbar);
2334 else if (format == SAVE_RAW_FORMAT) /* raw format */
2338 /* only forward direction */
2339 case SAVE_FORWARD_DIRECTION_MASK: {
2340 progbar_count = user_data->forward.saveinfo.count;
2341 progbar_quantum = user_data->forward.saveinfo.count/100;
2345 /* only reversed direction */
2346 case SAVE_REVERSE_DIRECTION_MASK: {
2347 progbar_count = user_data->reversed.saveinfo.count;
2348 progbar_quantum = user_data->reversed.saveinfo.count/100;
2356 destroy_progress_dlg(progbar);
2363 /* XXX how do you just copy the file? */
2364 while ((rread = read(fd, pd, 1)) > 0) {
2367 if((count > progbar_nextstep) && (count <= progbar_count)) {
2368 update_progress_dlg(progbar,
2369 (gfloat) count/progbar_count, "Saving");
2370 progbar_nextstep = progbar_nextstep + progbar_quantum;
2374 rwritten = ws_write(to_fd, pd, 1);
2376 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2380 destroy_progress_dlg(progbar);
2386 destroy_progress_dlg(progbar);
2394 /****************************************************************************/
2395 /* the user wants to save in a file */
2396 /* XXX support for different formats is currently commented out */
2397 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2400 /*GtkWidget *wav, *sw;*/
2401 GtkWidget *au, *raw;
2402 GtkWidget *rev, *forw, *both;
2403 user_data_t *user_data;
2404 gint channels , format;
2406 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2408 /* Perhaps the user specified a directory instead of a file.
2409 Check whether they did. */
2410 if (test_for_directory(g_dest) == EISDIR) {
2411 /* It's a directory - set the file selection box to display it. */
2412 set_last_open_dir(g_dest);
2413 file_selection_set_current_folder(fc, get_last_open_dir());
2414 gtk_file_chooser_set_current_name(fc, "");
2416 return FALSE; /* run the dialog again */
2420 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2421 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2423 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2424 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2425 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2426 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2427 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2428 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2430 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2431 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2432 * disable the ok button or disable the buttons for direction if only one is not ok. The
2433 * problem is if we open the save voice dialog and then click the refresh button and maybe
2434 * the state changes, so we can't save anymore. In this case we should be able to update
2435 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2438 /* we can not save in both directions */
2439 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2440 /* there are many combinations here, we just exit when first matches */
2441 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2442 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2443 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2444 "Can't save in a file: Unsupported codec!");
2445 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2446 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2447 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2448 "Can't save in a file: Wrong length of captured packets!");
2449 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2450 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2451 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2452 "Can't save in a file: Not all data in all packets was captured!");
2454 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2455 "Can't save in a file: File I/O problem!");
2457 return TRUE; /* we're done */
2459 /* we can not save forward direction */
2460 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2461 (GTK_TOGGLE_BUTTON (both)->active))) {
2462 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2463 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2464 "Can't save forward direction in a file: Unsupported codec!");
2465 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2466 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2467 "Can't save forward direction in a file: Wrong length of captured packets!");
2468 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2469 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2470 "Can't save forward direction in a file: Not all data in all packets was captured!");
2472 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2473 "Can't save forward direction in a file: File I/O problem!");
2475 return TRUE; /* we're done */
2477 /* we can not save reversed direction */
2478 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2479 (GTK_TOGGLE_BUTTON (both)->active))) {
2480 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2481 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2482 "Can't save reversed direction in a file: Unsupported codec!");
2483 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2484 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2485 "Can't save reversed direction in a file: Wrong length of captured packets!");
2486 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2487 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2488 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2489 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2490 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2491 "Can't save reversed direction in a file: No IAX2 data!");
2493 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2494 "Can't save reversed direction in a file: File I/O problem!");
2496 return TRUE; /* we're done */
2500 if (GTK_TOGGLE_BUTTON (wav)->active)
2501 format = SAVE_WAV_FORMAT;
2504 if (GTK_TOGGLE_BUTTON (au)->active)
2505 format = SAVE_AU_FORMAT;
2507 else if (GTK_TOGGLE_BUTTON (sw)->active)
2508 format = SAVE_SW_FORMAT;
2510 else if (GTK_TOGGLE_BUTTON (raw)->active)
2511 format = SAVE_RAW_FORMAT;
2513 format = SAVE_NONE_FORMAT;
2515 if (GTK_TOGGLE_BUTTON (rev)->active)
2516 channels = SAVE_REVERSE_DIRECTION_MASK;
2517 else if (GTK_TOGGLE_BUTTON (both)->active)
2518 channels = SAVE_BOTH_DIRECTION_MASK;
2520 channels = SAVE_FORWARD_DIRECTION_MASK;
2522 /* direction/format validity*/
2523 if (format == SAVE_AU_FORMAT)
2525 /* make sure streams are alaw/ulaw */
2526 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2527 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2528 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2530 return TRUE; /* we're done */
2532 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2533 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2534 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2536 return TRUE; /* we're done */
2538 /* make sure pt's don't differ */
2539 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2540 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2541 "Can't save in a file: Forward and reverse direction differ in type");
2543 return TRUE; /* we're done */
2546 else if (format == SAVE_RAW_FORMAT)
2548 /* can't save raw in both directions */
2549 if (channels == SAVE_BOTH_DIRECTION_MASK){
2550 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2551 "Can't save in a file: Unable to save raw data in both directions");
2553 return TRUE; /* we're done */
2558 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2559 "Can't save in a file: Invalid save format");
2561 return TRUE; /* we're done */
2564 if(!copy_file(g_dest, channels, format, user_data)) {
2565 /* XXX - report the error type! */
2566 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2567 "An error occurred while saving voice in a file!");
2569 return TRUE; /* we're done */
2572 return TRUE; /* we're done */
2575 /****************************************************************************/
2576 /* when the user wants to save the voice information in a file */
2577 /* XXX support for different formats is currently commented out */
2578 static void save_voice_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2582 GtkWidget *label_format;
2583 GtkWidget *channels_label;
2584 GSList *format_group = NULL;
2585 GSList *channels_group = NULL;
2586 GtkWidget *forward_rb;
2587 GtkWidget *reversed_rb;
2589 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2593 /* if we can't save in a file: wrong codec, cut packets or other errors */
2594 /* should the error arise here or later when you click ok button ?
2595 * if we do it here, then we must disable the refresh button, so we don't do it here */
2597 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2598 if (user_data->dlg.save_voice_as_w != NULL) {
2599 /* There's already a Save voice info dialog box; reactivate it. */
2600 reactivate_window(user_data->dlg.save_voice_as_w);
2604 /* XXX - use file_selection from dlg_utils instead! */
2605 user_data->dlg.save_voice_as_w =
2606 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2607 GTK_WINDOW(user_data->dlg.notebook),
2608 GTK_FILE_CHOOSER_ACTION_SAVE,
2609 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2610 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2612 #if GTK_CHECK_VERSION(2,8,0)
2613 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2615 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2617 /* Container for each row of widgets */
2618 vertb = gtk_vbox_new(FALSE, 0);
2619 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2620 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2621 gtk_widget_show (vertb);
2623 table1 = gtk_table_new (2, 4, FALSE);
2624 gtk_widget_show (table1);
2625 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2626 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2627 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2630 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2631 gtk_widget_show (label_format);
2632 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2633 (GtkAttachOptions) (GTK_FILL),
2634 (GtkAttachOptions) (0), 0, 0);
2637 label_format = gtk_label_new ("Format: ");
2638 gtk_widget_show (label_format);
2639 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2640 (GtkAttachOptions) (GTK_FILL),
2641 (GtkAttachOptions) (0), 0, 0);
2643 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2645 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2646 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2647 gtk_widget_show (raw_rb);
2648 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2649 (GtkAttachOptions) (GTK_FILL),
2650 (GtkAttachOptions) (0), 0, 0);
2653 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2654 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2655 gtk_widget_show (au_rb);
2656 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2657 (GtkAttachOptions) (GTK_FILL),
2658 (GtkAttachOptions) (0), 0, 0);
2661 /* we support .au - ulaw*/
2662 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2663 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2664 gtk_widget_show (wav_rb);
2665 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2666 (GtkAttachOptions) (GTK_FILL),
2667 (GtkAttachOptions) (0), 0, 0);
2669 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2670 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2671 gtk_widget_show (sw_rb);
2672 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2673 (GtkAttachOptions) (GTK_FILL),
2674 (GtkAttachOptions) (0), 0, 0);
2675 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2676 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2677 gtk_widget_show (au_rb);
2678 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2679 (GtkAttachOptions) (GTK_FILL),
2680 (GtkAttachOptions) (0), 0, 0);
2683 channels_label = gtk_label_new ("Channels: ");
2684 gtk_widget_show (channels_label);
2685 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2686 (GtkAttachOptions) (GTK_FILL),
2687 (GtkAttachOptions) (0), 0, 0);
2688 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2690 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2691 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2692 gtk_widget_show (forward_rb);
2693 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2694 (GtkAttachOptions) (GTK_FILL),
2695 (GtkAttachOptions) (0), 0, 0);
2697 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2698 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2699 gtk_widget_show (reversed_rb);
2700 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2701 (GtkAttachOptions) (GTK_FILL),
2702 (GtkAttachOptions) (0), 0, 0);
2704 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2705 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2706 gtk_widget_show (both_rb);
2707 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2708 (GtkAttachOptions) (GTK_FILL),
2709 (GtkAttachOptions) (0), 0, 0);
2711 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2714 /* if one direction is nok we don't allow saving
2715 XXX this is not ok since the user can click the refresh button and cause changes
2716 but we can not update this window. So we move all the decision on the time the ok
2717 button is clicked */
2718 if (user_data->forward.saved == FALSE) {
2719 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2720 gtk_widget_set_sensitive(forward_rb, FALSE);
2721 gtk_widget_set_sensitive(both_rb, FALSE);
2723 else if (user_data->reversed.saved == FALSE) {
2724 gtk_widget_set_sensitive(reversed_rb, FALSE);
2725 gtk_widget_set_sensitive(both_rb, FALSE);
2729 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2730 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2731 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2732 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2733 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2734 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2735 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2736 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2738 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2739 G_CALLBACK(window_delete_event_cb), NULL);
2740 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2741 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2743 gtk_widget_show(user_data->dlg.save_voice_as_w);
2744 window_present(user_data->dlg.save_voice_as_w);
2746 /* "Run" the GtkFileChooserDialog. */
2747 /* Upon exit: If "Accept" run the OK callback. */
2748 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2749 /* Destroy the window. */
2750 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2751 /* return with a TRUE status so that the dialog window will be destroyed. */
2752 /* Trying to re-run the dialog after popping up an alert box will not work */
2753 /* since the user will not be able to dismiss the alert box. */
2754 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2755 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2757 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2758 /* GtkFileChooserDialog. */
2759 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2760 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2761 break; /* we're done */
2764 window_destroy(user_data->dlg.save_voice_as_w);
2768 /****************************************************************************/
2769 /* when we are finished with redisection, we add the label for the statistic */
2770 static void draw_stat(user_data_t *user_data)
2772 gchar label_max[200];
2774 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2775 user_data->forward.statinfo.total_nr,
2776 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2778 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2780 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2781 user_data->reversed.statinfo.total_nr,
2782 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2784 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2791 /****************************************************************************/
2792 /* append a line to list */
2793 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2794 double delta, double jitter, double bandwidth, gchar *status,
2795 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2797 GtkListStore *list_store;
2799 if (strcmp(status, OK_TEXT) != 0) {
2800 user_data->dlg.number_of_nok++;
2803 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2805 /* Creates a new row at position. iter will be changed to point to this new row.
2806 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2807 * The row will be filled with the values given to this function.
2809 * should generally be preferred when inserting rows in a sorted list store.
2811 #if GTK_CHECK_VERSION(2,6,0)
2812 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2813 PACKET_COLUMN, number,
2814 DELTA_COLUMN, delta,
2815 JITTER_COLUMN, jitter,
2816 IPBW_COLUMN, bandwidth,
2817 STATUS_COLUMN, (char *)status,
2818 DATE_COLUMN, (char *)timeStr,
2819 LENGTH_COLUMN, pkt_len,
2820 FOREGROUND_COLOR_COL, NULL,
2821 BACKGROUND_COLOR_COL, (char *)color_str,
2825 gtk_list_store_append (list_store, &user_data->dlg.iter);
2826 gtk_list_store_set (list_store, &user_data->dlg.iter,
2827 PACKET_COLUMN, number,
2828 DELTA_COLUMN, delta,
2829 JITTER_COLUMN, jitter,
2830 IPBW_COLUMN, bandwidth,
2831 STATUS_COLUMN, (char *)status,
2832 DATE_COLUMN, (char *)timeStr,
2833 LENGTH_COLUMN, pkt_len,
2834 FOREGROUND_COLOR_COL, NULL,
2835 BACKGROUND_COLOR_COL, (char *)color_str,
2838 if(flags & STAT_FLAG_FIRST){
2839 /* Set first row as active */
2840 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2844 /****************************************************************************
2845 * Functions needed to present values from the list
2848 /* Present floats with two decimals */
2850 iax2_float_data_func (GtkTreeViewColumn *column _U_,
2851 GtkCellRenderer *renderer,
2852 GtkTreeModel *model,
2860 /* the col to get data from is in userdata */
2861 gint float_col = GPOINTER_TO_INT(user_data);
2863 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
2865 /* save the current locale */
2866 savelocale = setlocale(LC_NUMERIC, NULL);
2867 /* switch to "C" locale to avoid problems with localized decimal separators
2868 * in g_snprintf("%f") functions
2870 setlocale(LC_NUMERIC, "C");
2872 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
2873 /* restore previous locale setting */
2874 setlocale(LC_NUMERIC, savelocale);
2876 g_object_set(renderer, "text", buf, NULL);
2882 GtkWidget* create_list(user_data_t* user_data)
2885 GtkListStore *list_store;
2887 GtkTreeViewColumn *column;
2888 GtkCellRenderer *renderer;
2889 GtkTreeSortable *sortable;
2890 GtkTreeView *list_view;
2891 GtkTreeSelection *selection;
2893 /* Create the store */
2894 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2895 G_TYPE_UINT, /* Packet */
2896 G_TYPE_FLOAT, /* Delta(ms) */
2897 G_TYPE_FLOAT, /* Jitter(ms) */
2898 G_TYPE_FLOAT, /* IP BW(kbps) */
2899 G_TYPE_STRING, /* Status */
2900 G_TYPE_STRING, /* Date */
2901 G_TYPE_UINT, /* Length */
2902 G_TYPE_STRING, /* Foreground color */
2903 G_TYPE_STRING); /* Background color */
2906 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2908 list_view = GTK_TREE_VIEW(list);
2909 sortable = GTK_TREE_SORTABLE(list_store);
2911 #if GTK_CHECK_VERSION(2,6,0)
2912 /* Speed up the list display */
2913 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2916 /* Setup the sortable columns */
2917 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2918 gtk_tree_view_set_headers_clickable(list_view, FALSE);
2920 /* The view now holds a reference. We can get rid of our own reference */
2921 g_object_unref (G_OBJECT (list_store));
2924 * Create the first column packet, associating the "text" attribute of the
2925 * cell_renderer to the first column of the model
2927 renderer = gtk_cell_renderer_text_new ();
2928 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
2929 "text", PACKET_COLUMN,
2930 "foreground", FOREGROUND_COLOR_COL,
2931 "background", BACKGROUND_COLOR_COL,
2933 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2934 gtk_tree_view_column_set_resizable(column, TRUE);
2935 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2936 gtk_tree_view_column_set_min_width(column, 100);
2938 /* Add the column to the view. */
2939 gtk_tree_view_append_column (list_view, column);
2941 /* Second column.. Delta(ms). */
2942 renderer = gtk_cell_renderer_text_new ();
2943 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
2944 "text", DELTA_COLUMN,
2945 "foreground", FOREGROUND_COLOR_COL,
2946 "background", BACKGROUND_COLOR_COL,
2949 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2950 GINT_TO_POINTER(DELTA_COLUMN), NULL);
2952 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
2953 gtk_tree_view_column_set_resizable(column, TRUE);
2954 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2955 gtk_tree_view_column_set_min_width(column, 100);
2956 gtk_tree_view_append_column (list_view, column);
2958 /* Third column.. Jitter(ms). */
2959 renderer = gtk_cell_renderer_text_new ();
2960 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
2961 "text", JITTER_COLUMN,
2962 "foreground", FOREGROUND_COLOR_COL,
2963 "background", BACKGROUND_COLOR_COL,
2966 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2967 GINT_TO_POINTER(JITTER_COLUMN), NULL);
2969 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
2970 gtk_tree_view_column_set_resizable(column, TRUE);
2971 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2972 gtk_tree_view_column_set_min_width(column, 100);
2973 gtk_tree_view_append_column (list_view, column);
2975 /* Fourth column.. IP BW(kbps). */
2976 renderer = gtk_cell_renderer_text_new ();
2977 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
2978 "text", IPBW_COLUMN,
2979 "foreground", FOREGROUND_COLOR_COL,
2980 "background", BACKGROUND_COLOR_COL,
2983 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2984 GINT_TO_POINTER(IPBW_COLUMN), NULL);
2986 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
2987 gtk_tree_view_column_set_resizable(column, TRUE);
2988 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2989 gtk_tree_view_column_set_min_width(column, 100);
2990 gtk_tree_view_append_column (list_view, column);
2992 /* Fifth column.. Status. */
2993 renderer = gtk_cell_renderer_text_new ();
2994 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
2995 "text", STATUS_COLUMN,
2996 "foreground", FOREGROUND_COLOR_COL,
2997 "background", BACKGROUND_COLOR_COL,
2999 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3000 gtk_tree_view_column_set_resizable(column, TRUE);
3001 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3002 gtk_tree_view_column_set_min_width(column, 100);
3003 gtk_tree_view_append_column (list_view, column);
3005 /* Sixth column.. Length. */
3006 renderer = gtk_cell_renderer_text_new ();
3007 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3008 "text", LENGTH_COLUMN,
3009 "foreground", FOREGROUND_COLOR_COL,
3010 "background", BACKGROUND_COLOR_COL,
3014 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3015 gtk_tree_view_column_set_resizable(column, TRUE);
3016 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3017 gtk_tree_view_column_set_min_width(column, 100);
3018 gtk_tree_view_append_column (list_view, column);
3020 /* Now enable the sorting of each column */
3021 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3022 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3024 /* Setup the selection handler */
3025 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3026 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3028 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3029 G_CALLBACK (on_list_select_row),
3036 /****************************************************************************/
3037 /* Create the dialog box with all widgets */
3038 static void create_iax2_dialog(user_data_t* user_data)
3040 GtkWidget *window = NULL;
3041 GtkWidget *list_fwd;
3042 GtkWidget *list_rev;
3043 GtkWidget *label_stats_fwd;
3044 GtkWidget *label_stats_rev;
3045 GtkWidget *notebook;
3047 GtkWidget *main_vb, *page, *page_r;
3049 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3050 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3051 GtkWidget *graph_bt;
3052 gchar label_forward[150];
3053 gchar label_reverse[150];
3055 gchar str_ip_src[16];
3056 gchar str_ip_dst[16];
3058 window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
3059 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3061 /* Container for each row of widgets */
3062 main_vb = gtk_vbox_new(FALSE, 2);
3063 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3064 gtk_container_add(GTK_CONTAINER(window), main_vb);
3065 gtk_widget_show(main_vb);
3068 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3069 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3071 g_snprintf(label_forward, sizeof(label_forward),
3072 "Analysing stream from %s port %u to %s port %u ",
3073 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3076 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3077 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3079 g_snprintf(label_reverse, sizeof(label_reverse),
3080 "Analysing stream from %s port %u to %s port %u ",
3081 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3083 /* Start a notebook for flipping between sets of changes */
3084 notebook = gtk_notebook_new();
3085 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3086 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3088 user_data->dlg.notebook_signal_id =
3089 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3091 /* page for forward connection */
3092 page = gtk_vbox_new(FALSE, 8);
3093 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3095 /* direction label */
3096 label = gtk_label_new(label_forward);
3097 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3099 /* place for some statistics */
3100 label_stats_fwd = gtk_label_new("\n");
3101 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3103 /* scrolled window */
3104 scrolled_window = scrolled_window_new(NULL, NULL);
3107 list_fwd = create_list(user_data);
3108 gtk_widget_show(list_fwd);
3109 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3110 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3111 gtk_widget_show(scrolled_window);
3114 label = gtk_label_new(" Forward Direction ");
3115 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3117 /* same page for reversed connection */
3118 page_r = gtk_vbox_new(FALSE, 8);
3119 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3120 label = gtk_label_new(label_reverse);
3121 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3122 label_stats_rev = gtk_label_new("\n");
3123 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3125 scrolled_window_r = scrolled_window_new(NULL, NULL);
3127 list_rev = create_list(user_data);
3128 gtk_widget_show(list_rev);
3129 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3130 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3131 gtk_widget_show(scrolled_window_r);
3133 label = gtk_label_new(" Reversed Direction ");
3134 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3137 /* page for help&about or future */
3138 page_help = gtk_hbox_new(FALSE, 5);
3139 label = gtk_label_new(" Future ");
3140 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3141 frame = gtk_frame_new("");
3142 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3143 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3144 gtk_container_add(GTK_CONTAINER(frame), text);
3145 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3146 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3149 /* show all notebooks */
3150 gtk_widget_show_all(notebook);
3153 box4 = gtk_hbutton_box_new();
3154 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3155 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3156 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3157 gtk_box_set_spacing(GTK_BOX (box4), 0);
3158 gtk_widget_show(box4);
3160 voice_bt = gtk_button_new_with_label("Save payload...");
3161 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3162 gtk_widget_show(voice_bt);
3163 g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3165 csv_bt = gtk_button_new_with_label("Save as CSV...");
3166 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3167 gtk_widget_show(csv_bt);
3168 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3170 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3171 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3172 gtk_widget_show(refresh_bt);
3173 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3175 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3176 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3177 gtk_widget_show(goto_bt);
3178 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3180 graph_bt = gtk_button_new_with_label("Graph");
3181 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3182 gtk_widget_show(graph_bt);
3183 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3185 next_bt = gtk_button_new_with_label("Next non-Ok");
3186 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3187 gtk_widget_show(next_bt);
3188 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3190 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3191 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3192 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3193 gtk_widget_show(close_bt);
3194 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3196 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3197 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3199 gtk_widget_show(window);
3200 window_present(window);
3202 /* some widget references need to be saved for outside use */
3203 user_data->dlg.window = window;
3204 user_data->dlg.list_fwd = list_fwd;
3205 user_data->dlg.list_rev = list_rev;
3206 user_data->dlg.label_stats_fwd = label_stats_fwd;
3207 user_data->dlg.label_stats_rev = label_stats_rev;
3208 user_data->dlg.notebook = notebook;
3209 user_data->dlg.selected_list = list_fwd;
3210 user_data->dlg.number_of_nok = 0;
3213 * select the initial row
3215 gtk_widget_grab_focus(list_fwd);
3219 /****************************************************************************/
3220 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3221 const gchar* proto_field, guint32* p_result)
3224 proto_node *proto_sibling_node;
3225 header_field_info *hfssrc;
3228 finfo = PNODE_FINFO(ptree_node);
3230 if (hfinformation==(finfo->hfinfo)) {
3231 hfssrc = proto_registrar_get_byname(proto_field);
3232 if (hfssrc == NULL) {
3233 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3237 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3238 ptree_node=ptree_node->next) {
3239 finfo=PNODE_FINFO(ptree_node);
3240 if (hfssrc==finfo->hfinfo) {
3241 if (hfinformation->type==FT_IPv4) {
3242 ipv4 = fvalue_get(&finfo->value);
3243 *p_result = ipv4_get_net_order_addr(ipv4);
3246 *p_result = fvalue_get_uinteger(&finfo->value);
3255 proto_sibling_node = ptree_node->next;
3257 if (proto_sibling_node) {
3258 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3264 /****************************************************************************/
3265 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3266 const gchar* proto_name,
3267 const gchar* proto_field,
3270 proto_node *ptree_node;
3271 header_field_info *hfinformation;
3273 hfinformation = proto_registrar_get_byname(proto_name);
3274 if (hfinformation == NULL) {
3275 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3280 ptree_node = ((proto_node *)protocol_tree)->first_child;
3282 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3286 return process_node(ptree_node, hfinformation, proto_field, p_result);
3290 /****************************************************************************/
3292 address *ip_src_fwd,
3293 guint16 port_src_fwd,
3294 address *ip_dst_fwd,
3295 guint16 port_dst_fwd,
3296 address *ip_src_rev,
3297 guint16 port_src_rev,
3298 address *ip_dst_rev,
3299 guint16 port_dst_rev
3302 user_data_t *user_data;
3305 static color_t col[MAX_GRAPHS] = {
3306 {0, 0x0000, 0x0000, 0x0000},
3307 {0, 0xffff, 0x0000, 0x0000},
3308 {0, 0x0000, 0xffff, 0x0000},
3309 {0, 0x0000, 0x0000, 0xffff}
3314 user_data = g_malloc(sizeof(user_data_t));
3316 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3317 user_data->port_src_fwd = port_src_fwd;
3318 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3319 user_data->port_dst_fwd = port_dst_fwd;
3320 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3321 user_data->port_src_rev = port_src_rev;
3322 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3323 user_data->port_dst_rev = port_dst_rev;
3326 /* file names for storing sound data */
3327 /*XXX: check for errors*/
3328 fd = create_tempfile(&tempname, "wireshark_iax2_f");
3329 user_data->f_tempname = g_strdup(tempname);
3331 fd = create_tempfile(&tempname, "wireshark_iax2_r");
3332 user_data->r_tempname = g_strdup(tempname);
3334 user_data->forward.saveinfo.fp = NULL;
3335 user_data->reversed.saveinfo.fp = NULL;
3336 user_data->dlg.save_voice_as_w = NULL;
3337 user_data->dlg.save_csv_as_w = NULL;
3338 user_data->dlg.dialog_graph.window = NULL;
3340 /* init dialog_graph */
3341 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3342 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3343 user_data->dlg.dialog_graph.draw_area=NULL;
3344 user_data->dlg.dialog_graph.pixmap=NULL;
3345 user_data->dlg.dialog_graph.scrollbar=NULL;
3346 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3347 user_data->dlg.dialog_graph.pixmap_width=500;
3348 user_data->dlg.dialog_graph.pixmap_height=200;
3349 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3350 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3351 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3352 user_data->dlg.dialog_graph.max_interval=0;
3353 user_data->dlg.dialog_graph.num_items=0;
3354 user_data->dlg.dialog_graph.start_time = -1;
3356 for(i=0;i<MAX_GRAPHS;i++){
3357 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3358 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3359 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3360 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3361 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3362 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3363 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3364 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3367 /* create the dialog box */
3368 create_iax2_dialog(user_data);
3370 /* proceed as if the Refresh button would have been pressed */
3371 on_refresh_bt_clicked(NULL, user_data);
3374 /****************************************************************************/
3375 /* entry point from main menu */
3376 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3379 guint16 port_src_fwd;
3381 guint16 port_dst_fwd;
3383 guint16 port_src_rev;
3385 guint16 port_dst_rev;
3386 /* unsigned int ptype; */
3388 gchar filter_text[256];
3394 gboolean frame_matched;
3396 GList *strinfo_list;
3397 GList *filtered_list = NULL;
3398 rtp_stream_info_t *strinfo;
3401 /* Try to compile the filter. */
3402 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3403 if (!dfilter_compile(filter_text, &sfcode)) {
3404 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3407 /* we load the current file into cf variable */
3409 fdata = cf->current_frame;
3411 /* we are on the selected frame now */
3413 return; /* if we exit here it's an error */
3415 /* dissect the current frame */
3416 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3417 cf->pd, fdata->cap_len, &err, &err_info)) {
3418 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3419 cf_read_error_message(err, err_info), cf->filename);
3422 epan_dissect_init(&edt, TRUE, FALSE);
3423 epan_dissect_prime_dfilter(&edt, sfcode);
3424 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3425 frame_matched = dfilter_apply_edt(sfcode, &edt);
3427 /* if it is not an iax2 frame, show an error dialog */
3428 frame_matched = dfilter_apply_edt(sfcode, &edt);
3429 if (frame_matched != 1) {
3430 epan_dissect_cleanup(&edt);
3431 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3432 "You didn't choose a IAX2 packet!");
3435 /* check if it is Voice or MiniPacket
3436 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3437 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3438 "Please select a Voice packet!");
3442 /* check if it is part of a Call */
3443 if (edt.pi.circuit_id == 0) {
3444 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3445 "Please select a Call packet!");
3449 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3450 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3451 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3452 port_src_fwd = edt.pi.srcport;
3453 port_dst_fwd = edt.pi.destport;
3455 /* assume the inverse ip/port combination for the reverse direction */
3456 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3457 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3458 port_src_rev = edt.pi.destport;
3459 port_dst_rev = edt.pi.srcport;
3461 /* Scan for rtpstream */
3463 /* search for reversed direction in the global rtp streams list */
3465 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3466 while (strinfo_list)
3468 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3469 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3470 && strinfo->src_port==port_src_fwd
3471 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3472 && strinfo->dest_port==port_dst_fwd)
3474 filtered_list = g_list_prepend(filtered_list, strinfo);
3477 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3478 && strinfo->src_port==port_src_rev
3479 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3480 && strinfo->dest_port==port_dst_rev)
3483 filtered_list = g_list_append(filtered_list, strinfo);
3486 strinfo_list = g_list_next(strinfo_list);
3489 /* if more than one reverse streams found, we let the user choose the right one */
3491 rtpstream_dlg_show(filtered_list);
3508 /****************************************************************************/
3510 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3512 iax2_analysis_cb(NULL, NULL);
3515 /****************************************************************************/
3517 register_tap_listener_iax2_analysis(void)
3519 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3521 register_stat_menu_item("IA_X2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3522 iax2_analysis_cb, NULL, NULL, NULL);
3532 * indent-tabs-mode: t
3535 * ex: set shiftwidth=8 tabstop=8 noexpandtab
3536 * :indentSize=8:tabSize=8:noTabs=false: