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"
88 #include "gtk/utf8_entities.h"
100 BACKGROUND_COLOR_COL,
101 N_COLUMN /* The number of columns */
104 /****************************************************************************/
106 typedef struct column_arrows {
108 GtkWidget *ascend_pm;
109 GtkWidget *descend_pm;
113 #define NUM_GRAPH_ITEMS 100000
114 #define MAX_YSCALE 17
115 #define AUTO_MAX_YSCALE_INDEX 0
116 #define AUTO_MAX_YSCALE 0
118 #define GRAPH_FWD_JITTER 0
119 #define GRAPH_FWD_DIFF 1
120 #define GRAPH_REV_JITTER 2
121 #define GRAPH_REV_DIFF 3
122 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000};
124 #define MAX_PIXELS_PER_TICK 4
125 #define DEFAULT_PIXELS_PER_TICK_INDEX 1
126 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
127 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
129 #define MAX_TICK_VALUES 5
130 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
131 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
132 typedef struct _dialog_graph_graph_item_t {
135 } dialog_graph_graph_item_t;
137 typedef struct _dialog_graph_graph_t {
138 struct _user_data_t *ud;
139 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
142 GtkWidget *display_button;
147 } dialog_graph_graph_t;
150 typedef struct _dialog_graph_t {
151 gboolean needs_redraw;
152 gint32 interval_index; /* index into tick_interval_values array */
153 gint32 interval; /* measurement interval in ms */
154 guint32 last_interval;
155 guint32 max_interval; /* XXX max_interval and num_items are redundant */
157 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
159 GtkWidget *draw_area;
161 GtkAdjustment *scrollbar_adjustment;
162 GtkWidget *scrollbar;
165 int pixels_per_tick_index; /* index into pixels_per_tick array */
167 int max_y_units_index; /* index into yscale_max array */
172 typedef struct _dialog_data_t {
177 GtkWidget *label_stats_fwd;
178 GtkWidget *label_stats_rev;
179 GtkWidget *selected_list;
181 GtkTreeSelection *selected_list_sel;
182 gint selected_list_row;
184 GtkWidget *save_voice_as_w;
185 GtkWidget *save_csv_as_w;
186 gint notebook_signal_id;
187 dialog_graph_t dialog_graph;
190 #define OK_TEXT "[ Ok ]"
192 /* type of error when saving voice in a file didn't succeed */
195 TAP_RTP_WRONG_LENGTH,
196 TAP_RTP_PADDING_ERROR,
198 TAP_RTP_FILE_OPEN_ERROR,
202 typedef struct _tap_iax2_save_info_t {
205 error_type_t error_type;
207 } tap_iax2_save_info_t;
210 /* structure that holds the information about the forward and reversed direction */
211 struct _info_direction {
212 tap_iax2_stat_t statinfo;
213 tap_iax2_save_info_t saveinfo;
216 #define SILENCE_PCMU (guint8)0xFF
217 #define SILENCE_PCMA (guint8)0x55
219 /* structure that holds general information about the connection
220 * and structures for both directions */
221 typedef struct _user_data_t {
222 /* tap associated data*/
224 guint16 port_src_fwd;
226 guint16 port_dst_fwd;
228 guint16 port_src_rev;
230 guint16 port_dst_rev;
232 struct _info_direction forward;
233 struct _info_direction reversed;
238 /* dialog associated data */
245 static const gchar *titles[7] = {
255 #define SAVE_FORWARD_DIRECTION_MASK 0x01
256 #define SAVE_REVERSE_DIRECTION_MASK 0x02
257 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
259 #define SAVE_NONE_FORMAT 0
260 #define SAVE_WAV_FORMAT 1
261 #define SAVE_AU_FORMAT 2
262 #define SAVE_SW_FORMAT 3
263 #define SAVE_RAW_FORMAT 4
266 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
267 /****************************************************************************/
268 static void enable_graph(dialog_graph_graph_t *dgg)
275 static void dialog_graph_reset(user_data_t* user_data);
279 /****************************************************************************/
282 /****************************************************************************/
283 /* when there is a [re]reading of packet's */
285 iax2_reset(void *user_data_arg)
287 user_data_t *user_data = user_data_arg;
288 user_data->forward.statinfo.first_packet = TRUE;
289 user_data->reversed.statinfo.first_packet = TRUE;
290 user_data->forward.statinfo.max_delta = 0;
291 user_data->reversed.statinfo.max_delta = 0;
292 user_data->forward.statinfo.max_jitter = 0;
293 user_data->reversed.statinfo.max_jitter = 0;
294 user_data->forward.statinfo.mean_jitter = 0;
295 user_data->reversed.statinfo.mean_jitter = 0;
296 user_data->forward.statinfo.delta = 0;
297 user_data->reversed.statinfo.delta = 0;
298 user_data->forward.statinfo.diff = 0;
299 user_data->reversed.statinfo.diff = 0;
300 user_data->forward.statinfo.jitter = 0;
301 user_data->reversed.statinfo.jitter = 0;
302 user_data->forward.statinfo.bandwidth = 0;
303 user_data->reversed.statinfo.bandwidth = 0;
304 user_data->forward.statinfo.total_bytes = 0;
305 user_data->reversed.statinfo.total_bytes = 0;
306 user_data->forward.statinfo.bw_start_index = 0;
307 user_data->reversed.statinfo.bw_start_index = 0;
308 user_data->forward.statinfo.bw_index = 0;
309 user_data->reversed.statinfo.bw_index = 0;
310 user_data->forward.statinfo.timestamp = 0;
311 user_data->reversed.statinfo.timestamp = 0;
312 user_data->forward.statinfo.max_nr = 0;
313 user_data->reversed.statinfo.max_nr = 0;
314 user_data->forward.statinfo.total_nr = 0;
315 user_data->reversed.statinfo.total_nr = 0;
316 user_data->forward.statinfo.sequence = 0;
317 user_data->reversed.statinfo.sequence = 0;
318 user_data->forward.statinfo.start_seq_nr = 0;
319 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
320 user_data->forward.statinfo.stop_seq_nr = 0;
321 user_data->reversed.statinfo.stop_seq_nr = 0;
322 user_data->forward.statinfo.cycles = 0;
323 user_data->reversed.statinfo.cycles = 0;
324 user_data->forward.statinfo.under = FALSE;
325 user_data->reversed.statinfo.under = FALSE;
326 user_data->forward.statinfo.start_time = 0;
327 user_data->reversed.statinfo.start_time = 0;
328 user_data->forward.statinfo.time = 0;
329 user_data->reversed.statinfo.time = 0;
330 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
331 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
333 user_data->forward.saveinfo.count = 0;
334 user_data->reversed.saveinfo.count = 0;
335 user_data->forward.saveinfo.saved = FALSE;
336 user_data->reversed.saveinfo.saved = FALSE;
338 /* clear the dialog box lists */
339 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
340 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
342 /* reset graph info */
343 dialog_graph_reset(user_data);
346 /* XXX check for error at fclose? */
347 if (user_data->forward.saveinfo.fp != NULL)
348 fclose(user_data->forward.saveinfo.fp);
349 if (user_data->reversed.saveinfo.fp != NULL)
350 fclose(user_data->reversed.saveinfo.fp);
351 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
352 if (user_data->forward.saveinfo.fp == NULL)
353 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
354 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
355 if (user_data->reversed.saveinfo.fp == NULL)
356 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
360 /****************************************************************************/
361 static int iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
363 dialog_graph_graph_item_t *it;
367 /* we sometimes get called when dgg is disabled.
368 this is a bug since the tap listener should be removed first */
373 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
376 * Find which interval this is supposed to to in and store the
377 * interval index as idx
379 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
380 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
382 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
386 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
388 /* some sanity checks */
389 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
393 /* update num_items */
394 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
395 dgg->ud->dlg.dialog_graph.num_items=idx;
396 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
400 * Find the appropriate dialog_graph_graph_item_t structure
405 * Use the max value to highlight RTP problems
407 if (value > it->value) {
410 it->flags = it->flags | statinfo->flags;
415 /****************************************************************************/
416 /* here we can redraw the output */
418 static void iax2_draw(void *prs _U_)
423 /* forward declarations */
424 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
425 double delta, double jitter, double bandwidth, gchar *status,
426 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
428 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
429 tap_iax2_stat_t *statinfo, packet_info *pinfo,
430 const struct _iax2_info_t *iax2info);
432 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
433 tap_iax2_stat_t *statinfo,
435 const struct _iax2_info_t *iax2info);
438 /****************************************************************************/
439 /* whenever a IAX2 packet is seen by the tap listener */
440 static int iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
442 user_data_t *user_data = user_data_arg;
443 const struct _iax2_info_t *iax2info = iax2info_arg;
445 /* we ignore packets that are not displayed */
446 if (pinfo->fd->flags.passed_dfilter == 0)
449 /* we ignore packets that carry no data */
450 if (iax2info->payload_len == 0)
453 /* is it the forward direction? */
454 else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
455 && user_data->port_src_fwd == pinfo->srcport
456 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
457 && user_data->port_dst_fwd == pinfo->destport) {
458 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
459 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.jitter*1000000));
460 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.diff*1000000));
461 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
462 &(user_data->forward.statinfo), pinfo, iax2info);
463 iax2_packet_save_payload(&(user_data->forward.saveinfo),
464 &(user_data->forward.statinfo), pinfo, iax2info);
466 /* is it the reversed direction? */
467 else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
468 && user_data->port_src_rev == pinfo->srcport
469 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
470 && user_data->port_dst_rev == pinfo->destport) {
471 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
472 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
473 iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
474 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
475 &(user_data->reversed.statinfo), pinfo, iax2info);
476 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
477 &(user_data->reversed.statinfo), pinfo, iax2info);
483 /****************************************************************************/
484 /* This comes from tap-rtp-common.c */
485 /****************************************************************************/
487 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
489 const struct _iax2_info_t *iax2info)
492 double current_jitter;
496 /* check payload type */
497 if (iax2info->ftype == AST_FRAME_VOICE) {
498 if (iax2info->csub != statinfo->pt)
499 statinfo->flags |= STAT_FLAG_PT_CHANGE;
500 statinfo->pt = iax2info->csub;
503 /* store the current time and calculate the current jitter */
504 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
505 current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
506 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
507 statinfo->delta = current_time-(statinfo->time);
508 statinfo->jitter = current_jitter;
509 statinfo->diff = current_diff;
511 /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
512 statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
513 statinfo->bw_history[statinfo->bw_index].time = current_time;
514 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
515 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
516 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
517 statinfo->bw_start_index++;
518 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
520 statinfo->total_bytes += iax2info->payload_len + 24;
521 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
522 statinfo->bw_index++;
523 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
526 /* is this the first packet we got in this direction? */
527 if (statinfo->first_packet) {
528 statinfo->start_seq_nr = 0;
529 statinfo->start_time = current_time;
531 statinfo->jitter = 0;
533 statinfo->flags |= STAT_FLAG_FIRST;
534 statinfo->first_packet = FALSE;
536 /* is it a regular packet? */
537 if (!(statinfo->flags & STAT_FLAG_FIRST)
538 && !(statinfo->flags & STAT_FLAG_MARKER)
539 && !(statinfo->flags & STAT_FLAG_PT_CN)
540 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
541 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
542 /* include it in maximum delta calculation */
543 if (statinfo->delta > statinfo->max_delta) {
544 statinfo->max_delta = statinfo->delta;
545 statinfo->max_nr = pinfo->fd->num;
547 /* maximum and mean jitter calculation */
548 if (statinfo->jitter > statinfo->max_jitter) {
549 statinfo->max_jitter = statinfo->jitter;
551 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
553 /* regular payload change? (CN ignored) */
554 if (!(statinfo->flags & STAT_FLAG_FIRST)
555 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
556 if ((statinfo->pt != statinfo->reg_pt)
557 && (statinfo->reg_pt != PT_UNDEFINED)) {
558 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
562 /* set regular payload*/
563 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
564 statinfo->reg_pt = statinfo->pt;
567 /* TODO: lost packets / duplicated: we should infer this from timestamp... */
568 statinfo->time = current_time;
569 statinfo->timestamp = iax2info->timestamp;
570 statinfo->stop_seq_nr = 0;
571 statinfo->total_nr++;
577 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
578 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
579 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
580 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
581 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
583 /****************************************************************************/
584 /* adds statistics information from the packet to the list */
585 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
586 tap_iax2_stat_t *statinfo, packet_info *pinfo,
587 const struct _iax2_info_t *iax2info _U_)
594 GdkColor color = COLOR_DEFAULT;
596 then = pinfo->fd->abs_ts.secs;
597 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
598 tm_tmp = localtime(&then);
599 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
602 tm_tmp->tm_year + 1900,
608 /* Default to using black on white text if nothing below overrides it */
609 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
611 if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
612 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
614 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
616 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
617 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
618 color = COLOR_WARNING;
619 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
621 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
622 g_snprintf(status,sizeof(status),"Incorrect timestamp");
623 color = COLOR_WARNING;
624 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
626 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
627 && !(statinfo->flags & STAT_FLAG_FIRST)
628 && !(statinfo->flags & STAT_FLAG_PT_CN)
629 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
630 && !(statinfo->flags & STAT_FLAG_MARKER)) {
631 g_snprintf(status,sizeof(status),"Marker missing?");
632 color = COLOR_WARNING;
633 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
636 if (statinfo->flags & STAT_FLAG_MARKER) {
637 color = COLOR_WARNING;
638 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
640 g_snprintf(status,sizeof(status),OK_TEXT);
642 /* is this the first packet we got in this direction? */
643 if (statinfo->flags & STAT_FLAG_FIRST) {
644 add_to_list(list, user_data,
650 timeStr, pinfo->fd->pkt_len,
655 add_to_list(list, user_data,
657 statinfo->delta*1000,
658 statinfo->jitter*1000,
661 timeStr, pinfo->fd->pkt_len,
668 #define MAX_SILENCE_TICKS 1000000
669 /****************************************************************************/
670 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
671 tap_iax2_stat_t *statinfo,
673 const struct _iax2_info_t *iax2info)
678 /* is this the first packet we got in this direction? */
679 if (statinfo->flags & STAT_FLAG_FIRST) {
680 if (saveinfo->fp == NULL) {
681 saveinfo->saved = FALSE;
682 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
685 saveinfo->saved = TRUE;
688 /* save the voice information */
689 /* if there was already an error, we quit */
690 if (saveinfo->saved == FALSE)
693 /* if the captured length and packet length aren't equal, we quit */
694 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
695 saveinfo->saved = FALSE;
696 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
700 if (iax2info->payload_len > 0) {
701 data = iax2info->payload_data;
702 nchars=fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
703 saveinfo->count+=iax2info->payload_len;
705 fflush(saveinfo->fp);
706 saveinfo->saved = TRUE;
714 /****************************************************************************/
717 /****************************************************************************/
719 /****************************************************************************/
720 /* close the dialog window and remove the tap listener */
721 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
723 /* remove tap listener */
724 protect_thread_critical_region();
725 remove_tap_listener(user_data);
726 unprotect_thread_critical_region();
728 /* close and remove temporary files */
729 if (user_data->forward.saveinfo.fp != NULL)
730 fclose(user_data->forward.saveinfo.fp);
731 if (user_data->reversed.saveinfo.fp != NULL)
732 fclose(user_data->reversed.saveinfo.fp);
733 /*XXX: test for error **/
734 ws_remove(user_data->f_tempname);
735 ws_remove(user_data->r_tempname);
737 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
738 /* destroy save_csv_as window if open */
739 if (user_data->dlg.save_csv_as_w != NULL)
740 window_destroy(user_data->dlg.save_csv_as_w);
742 /* destroy save_voice_as window if open */
743 if (user_data->dlg.save_voice_as_w != NULL)
744 window_destroy(user_data->dlg.save_voice_as_w);
746 /* destroy graph window if open */
747 if (user_data->dlg.dialog_graph.window != NULL)
748 window_destroy(user_data->dlg.dialog_graph.window);
751 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
752 /* XXX: Is this still true for GTK2 ??? */
753 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
755 g_free(user_data->f_tempname);
756 g_free(user_data->r_tempname);
761 /****************************************************************************/
762 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
763 GtkNotebookPage *page _U_,
765 user_data_t *user_data _U_)
767 user_data->dlg.selected_list =
768 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
770 user_data->dlg.selected_list_row = 0;
773 /****************************************************************************/
774 static void on_list_select_row(GtkTreeSelection *selection,
775 user_data_t *user_data _U_/*gpointer data */)
777 user_data->dlg.selected_list_sel = selection;
781 /****************************************************************************/
782 static void dialog_graph_set_title(user_data_t* user_data)
785 if (!user_data->dlg.dialog_graph.window){
788 title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
789 get_addr_name(&(user_data->ip_src_fwd)),
790 user_data->port_src_fwd,
791 get_addr_name(&(user_data->ip_dst_fwd)),
792 user_data->port_dst_fwd,
793 get_addr_name(&(user_data->ip_src_rev)),
794 user_data->port_src_rev,
795 get_addr_name(&(user_data->ip_dst_rev)),
796 user_data->port_dst_rev);
798 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
804 /****************************************************************************/
805 static void dialog_graph_reset(user_data_t* user_data)
809 user_data->dlg.dialog_graph.needs_redraw=TRUE;
810 for(i=0;i<MAX_GRAPHS;i++){
811 for(j=0;j<NUM_GRAPH_ITEMS;j++){
812 dialog_graph_graph_item_t *dggi;
813 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
818 user_data->dlg.dialog_graph.last_interval=0xffffffff;
819 user_data->dlg.dialog_graph.max_interval=0;
820 user_data->dlg.dialog_graph.num_items=0;
822 /* create the color titles near the filter buttons */
823 for(i=0;i<MAX_GRAPHS;i++){
826 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
827 sizeof (user_data->dlg.dialog_graph.graph[0].title),
828 "%s: %s:%u to %s:%u",
830 get_addr_name(&(user_data->ip_src_fwd)),
831 user_data->port_src_fwd,
832 get_addr_name(&(user_data->ip_dst_fwd)),
833 user_data->port_dst_fwd);
836 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
837 sizeof(user_data->dlg.dialog_graph.graph[0].title),
838 "%s: %s:%u to %s:%u",
840 get_addr_name(&(user_data->ip_src_rev)),
841 user_data->port_src_rev,
842 get_addr_name(&(user_data->ip_dst_rev)),
843 user_data->port_dst_rev);
847 dialog_graph_set_title(user_data);
850 /****************************************************************************/
851 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
853 dialog_graph_graph_item_t *it;
860 /****************************************************************************/
861 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
864 g_snprintf(buf, buf_len, "%ds",t/1000000);
865 } else if(t>=1000000){
866 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
868 g_snprintf(buf, buf_len, "%dms",t/1000);
870 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
872 g_snprintf(buf, buf_len, "%dus",t);
876 /****************************************************************************/
877 static void dialog_graph_draw(user_data_t* user_data)
880 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
881 gint32 current_interval;
882 guint32 left_x_border;
883 guint32 right_x_border;
884 guint32 top_y_border;
885 guint32 bottom_y_border;
887 int label_width, label_height;
888 int label_width_mid, label_height_mid;
889 guint32 draw_width, draw_height;
890 char label_string[15];
893 guint32 num_time_intervals;
894 guint32 max_value; /* max value of seen data */
895 guint32 max_y; /* max value of the Y scale */
897 if(!user_data->dlg.dialog_graph.needs_redraw){
900 user_data->dlg.dialog_graph.needs_redraw=FALSE;
903 * Find the length of the intervals we have data for
904 * so we know how large arrays we need to malloc()
906 num_time_intervals=user_data->dlg.dialog_graph.num_items;
907 /* if there isnt anything to do, just return */
908 if(num_time_intervals==0){
911 num_time_intervals+=1;
912 /* XXX move this check to _packet() */
913 if(num_time_intervals>NUM_GRAPH_ITEMS){
914 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
919 * find the max value so we can autoscale the y axis
922 for(i=0;i<MAX_GRAPHS;i++){
925 if(!user_data->dlg.dialog_graph.graph[i].display){
928 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
931 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
933 /* keep track of the max value we have encountered */
943 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
944 user_data->dlg.dialog_graph.draw_area->style->white_gc,
947 user_data->dlg.dialog_graph.draw_area->allocation.width,
948 user_data->dlg.dialog_graph.draw_area->allocation.height);
952 * Calculate the y scale we should use
954 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
955 max_y=yscale_max[MAX_YSCALE-1];
956 for(i=MAX_YSCALE-1;i>0;i--){
957 if(max_value<yscale_max[i]){
962 /* the user had specified an explicit y scale to use */
963 max_y=user_data->dlg.dialog_graph.max_y_units;
967 * Calculate size of borders surrounding the plot
968 * The border on the right side needs to be adjusted depending
969 * on the width of the text labels.
971 print_time_scale_string(label_string, sizeof(label_string), max_y);
972 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
973 pango_layout_get_pixel_size(layout, &label_width, &label_height);
974 print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
975 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
976 pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
977 if (label_width_mid > label_width) {
978 label_width = label_width_mid;
979 label_height = label_height_mid;
983 right_x_border=label_width+20;
985 bottom_y_border=label_height+20;
989 * Calculate the size of the drawing area for the actual plot
991 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
992 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
996 * Draw the y axis and labels
997 * (we always draw the y scale with 11 ticks along the axis)
999 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1000 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1002 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1003 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1009 /* first, middle and last tick are slightly longer */
1013 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1014 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1015 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1016 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1017 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1018 /* draw the labels */
1020 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1021 pango_layout_set_text(layout, label_string, -1);
1022 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1023 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1024 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1025 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1026 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1030 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1031 pango_layout_set_text(layout, label_string, -1);
1032 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1033 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1034 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1035 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1036 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1040 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1041 pango_layout_set_text(layout, label_string, -1);
1042 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1043 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1044 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1045 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1046 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1054 * if we have not specified the last_interval via the gui,
1055 * then just pick the current end of the capture so that is scrolls
1056 * nicely when doing live captures
1058 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1059 last_interval=user_data->dlg.dialog_graph.max_interval;
1061 last_interval=user_data->dlg.dialog_graph.last_interval;
1068 /* plot the x-scale */
1069 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc, left_x_border, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1, user_data->dlg.dialog_graph.pixmap_width-right_x_border+1, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1);
1071 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1072 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1073 first_interval*=user_data->dlg.dialog_graph.interval;
1080 while(interval_delta<((last_interval-first_interval)/10)){
1081 interval_delta*=delta_multiplier;
1082 if(delta_multiplier==5){
1089 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1092 /* if pixels_per_tick is <5, only draw every 10 ticks */
1093 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1097 if(current_interval%interval_delta){
1103 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1104 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1105 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1106 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1107 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1108 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1111 if(user_data->dlg.dialog_graph.interval>=1000){
1112 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1113 } else if(user_data->dlg.dialog_graph.interval>=100){
1114 g_snprintf(label_string, sizeof(label_string),
1115 "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1116 } else if(user_data->dlg.dialog_graph.interval>=10){
1117 g_snprintf(label_string, sizeof(label_string),
1118 "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1120 g_snprintf(label_string, sizeof(label_string),
1121 "%d.%3ds", current_interval/1000,current_interval%1000);
1123 pango_layout_set_text(layout, label_string, -1);
1124 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1125 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1126 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1127 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1128 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1140 * Draw "x" for Sequence Errors and "m" for Marks
1142 /* Draw the labels Fwd and Rev */
1143 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd", sizeof(label_string));
1144 pango_layout_set_text(layout, label_string, -1);
1145 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1146 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1147 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1148 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1149 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1151 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev", sizeof(label_string));
1152 pango_layout_set_text(layout, label_string, -1);
1153 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1154 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1155 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1156 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1157 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1160 /* Draw the marks */
1161 for(i=MAX_GRAPHS-1;i>=0;i--){
1163 guint32 x_pos, prev_x_pos;
1165 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1166 if (!user_data->dlg.dialog_graph.graph[i].display){
1169 /* initialize prev x/y to the low left corner of the graph */
1170 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1172 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1173 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1175 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1176 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1177 g_strlcpy(label_string, "x", sizeof(label_string));
1179 g_strlcpy(label_string, "m", sizeof(label_string));
1182 pango_layout_set_text(layout, label_string, -1);
1183 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1184 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1185 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1187 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1195 g_object_unref(G_OBJECT(layout));
1198 * Loop over all graphs and draw them
1200 for(i=MAX_GRAPHS-1;i>=0;i--){
1202 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1203 if (!user_data->dlg.dialog_graph.graph[i].display){
1206 /* initialize prev x/y to the low left corner of the graph */
1207 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1208 prev_y_pos=draw_height-1+top_y_border;
1210 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1212 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1213 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1217 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1220 /* dont need to draw anything if the segment
1221 * is entirely above the top of the graph
1223 if( (prev_y_pos==0) && (y_pos==0) ){
1230 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1231 x_pos, draw_height-1+top_y_border,
1241 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1242 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1243 user_data->dlg.dialog_graph.pixmap,
1246 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1249 /* update the scrollbar */
1250 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1251 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1252 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1253 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1254 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1256 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1258 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1259 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1260 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1264 /****************************************************************************/
1265 static void dialog_graph_redraw(user_data_t* user_data)
1267 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1268 dialog_graph_draw(user_data);
1271 /****************************************************************************/
1273 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1275 user_data->dlg.dialog_graph.window = NULL;
1278 /****************************************************************************/
1279 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1281 user_data_t *user_data;
1283 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1285 exit(10); /* !! XXX: should be g_assert or something ? */
1289 gdk_draw_pixmap(widget->window,
1290 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1291 user_data->dlg.dialog_graph.pixmap,
1292 event->area.x, event->area.y,
1293 event->area.x, event->area.y,
1294 event->area.width, event->area.height);
1299 /****************************************************************************/
1300 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1302 user_data_t *user_data;
1305 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1311 if(user_data->dlg.dialog_graph.pixmap){
1312 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1313 user_data->dlg.dialog_graph.pixmap=NULL;
1316 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1317 widget->allocation.width,
1318 widget->allocation.height,
1320 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1321 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1323 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1324 widget->style->white_gc,
1327 widget->allocation.width,
1328 widget->allocation.height);
1330 /* set up the colors and the GC structs for this pixmap */
1331 for(i=0;i<MAX_GRAPHS;i++){
1332 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1333 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1336 dialog_graph_redraw(user_data);
1340 /****************************************************************************/
1341 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1343 user_data_t *user_data=(user_data_t *)data;
1346 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1347 if(user_data->dlg.dialog_graph.last_interval==mi){
1350 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1351 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1355 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1357 dialog_graph_redraw(user_data);
1361 /****************************************************************************/
1362 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1364 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1365 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1366 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1368 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1370 /* signals needed to handle backing pixmap */
1371 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1372 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1374 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1375 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1377 /* create the associated scrollbar */
1378 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1379 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1380 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1381 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1382 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1385 /****************************************************************************/
1386 static void disable_graph(dialog_graph_graph_t *dgg)
1390 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1395 /****************************************************************************/
1396 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1398 /* this graph is not active, just update display and redraw */
1399 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1401 dialog_graph_redraw(dgg->ud);
1406 cf_retap_packets(&cfile);
1407 dialog_graph_redraw(dgg->ud);
1412 /****************************************************************************/
1413 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1419 hbox=gtk_hbox_new(FALSE, 3);
1420 gtk_container_add(GTK_CONTAINER(box), hbox);
1421 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1422 gtk_widget_show(hbox);
1424 g_snprintf(str, sizeof(str), "Graph %d", num);
1425 dgg->display_button=gtk_toggle_button_new_with_label(str);
1426 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1427 gtk_widget_show(dgg->display_button);
1428 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1429 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1431 label=gtk_label_new(dgg->title);
1432 gtk_widget_show(label);
1433 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1435 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1436 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1437 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1438 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1439 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1444 /****************************************************************************/
1445 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1452 frame=gtk_frame_new("Graphs");
1453 gtk_container_add(GTK_CONTAINER(box), frame);
1454 gtk_widget_show(frame);
1456 vbox=gtk_vbox_new(FALSE, 1);
1457 gtk_container_add(GTK_CONTAINER(frame), vbox);
1458 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1459 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1460 gtk_widget_show(vbox);
1462 for(i=0;i<MAX_GRAPHS;i++){
1463 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1466 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1467 gtk_widget_show(label);
1468 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1473 /****************************************************************************/
1474 static void yscale_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));
1482 user_data->dlg.dialog_graph.max_y_units_index=i;
1483 user_data->dlg.dialog_graph.max_y_units=yscale_max[i]/1000;
1484 dialog_graph_redraw(user_data);
1487 /****************************************************************************/
1488 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1491 user_data_t *user_data;
1493 user_data=(user_data_t *)key;
1494 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1495 user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1496 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1497 dialog_graph_redraw(user_data);
1500 /****************************************************************************/
1501 static void tick_interval_select(GtkWidget *item, gpointer key)
1504 user_data_t *user_data;
1506 user_data=(user_data_t *)key;
1507 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1509 user_data->dlg.dialog_graph.interval_index=i;
1510 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1511 cf_retap_packets(&cfile);
1512 dialog_graph_redraw(user_data);
1515 /****************************************************************************/
1517 create_yscale_max_menu_items(user_data_t* user_data)
1520 GtkWidget *combo_box;
1523 combo_box = gtk_combo_box_new_text ();
1525 for(i=0;i<MAX_YSCALE;i++){
1526 if(yscale_max[i]==AUTO_MAX_YSCALE){
1527 g_strlcpy(str,"Auto",sizeof(str));
1528 } else if (yscale_max[i] < 1000000) {
1529 g_snprintf(str, sizeof(str), "%u us", yscale_max[i]/1000);
1531 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000000);
1534 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1536 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1537 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1542 /****************************************************************************/
1544 create_pixels_per_tick_menu_items(user_data_t *user_data)
1547 GtkWidget *combo_box;
1550 combo_box = gtk_combo_box_new_text ();
1552 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1553 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1554 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1556 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1558 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1563 /****************************************************************************/
1565 create_tick_interval_menu_items(user_data_t *user_data)
1567 GtkWidget *combo_box;
1571 combo_box = gtk_combo_box_new_text ();
1573 for(i=0;i<MAX_TICK_VALUES;i++){
1574 if(tick_interval_values[i]>=1000){
1575 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1576 } else if(tick_interval_values[i]>=100){
1577 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1578 } else if(tick_interval_values[i]>=10){
1579 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1581 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1583 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1585 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1586 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1591 /****************************************************************************/
1592 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1596 GtkWidget *combo_box;
1598 hbox=gtk_hbox_new(FALSE, 0);
1599 gtk_container_add(GTK_CONTAINER(box), hbox);
1600 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1601 gtk_widget_show(hbox);
1603 label=gtk_label_new(name);
1604 gtk_widget_show(label);
1605 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1607 combo_box = (*func)(user_data);
1608 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1609 gtk_widget_show(combo_box);
1612 /****************************************************************************/
1613 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1615 GtkWidget *frame_vbox;
1619 frame_vbox=gtk_vbox_new(FALSE, 0);
1620 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1621 gtk_widget_show(frame_vbox);
1623 frame = gtk_frame_new("X Axis");
1624 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1625 gtk_widget_show(frame);
1627 vbox=gtk_vbox_new(FALSE, 0);
1628 gtk_container_add(GTK_CONTAINER(frame), vbox);
1629 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1630 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1631 gtk_widget_show(vbox);
1633 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1634 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1636 frame = gtk_frame_new("Y Axis");
1637 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1638 gtk_widget_show(frame);
1640 vbox=gtk_vbox_new(FALSE, 0);
1641 gtk_container_add(GTK_CONTAINER(frame), vbox);
1642 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1643 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1644 gtk_widget_show(vbox);
1646 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1651 /****************************************************************************/
1652 static void dialog_graph_init_window(user_data_t* user_data)
1656 GtkWidget *bt_close;
1658 /* create the main window */
1659 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1661 vbox=gtk_vbox_new(FALSE, 0);
1662 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1663 gtk_widget_show(vbox);
1665 create_draw_area(user_data, vbox);
1667 hbox=gtk_hbox_new(FALSE, 3);
1668 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1669 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1670 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1671 gtk_widget_show(hbox);
1673 create_filter_area(user_data, hbox);
1674 create_ctrl_area(user_data, hbox);
1676 dialog_graph_set_title(user_data);
1678 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1679 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1680 gtk_widget_show(hbox);
1682 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1683 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1685 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1687 gtk_widget_show(user_data->dlg.dialog_graph.window);
1688 window_present(user_data->dlg.dialog_graph.window);
1693 /****************************************************************************/
1694 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1696 if (user_data->dlg.dialog_graph.window != NULL) {
1697 /* There's already a graph window; reactivate it. */
1698 reactivate_window(user_data->dlg.dialog_graph.window);
1702 dialog_graph_init_window(user_data);
1706 /****************************************************************************/
1707 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1710 GtkTreeModel *model;
1711 GtkTreeSelection *selection;
1714 selection = user_data->dlg.selected_list_sel;
1716 if (selection==NULL)
1719 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1720 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1721 cf_goto_frame(&cfile, fnumber);
1726 static void draw_stat(user_data_t *user_data);
1728 /****************************************************************************/
1729 /* re-dissects all packets */
1730 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1732 GString *error_string;
1734 /* remove tap listener */
1735 protect_thread_critical_region();
1736 remove_tap_listener(user_data);
1737 unprotect_thread_critical_region();
1739 /* register tap listener */
1740 error_string = register_tap_listener("IAX2", user_data, NULL, 0,
1741 iax2_reset, iax2_packet, iax2_draw);
1742 if (error_string != NULL) {
1743 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1744 g_string_free(error_string, TRUE);
1748 /* retap all packets */
1749 cf_retap_packets(&cfile);
1751 /* draw statistics info */
1752 draw_stat(user_data);
1756 /****************************************************************************/
1757 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1760 GtkTreeModel *model;
1762 GtkTreeSelection *selection;
1765 selection = user_data->dlg.selected_list_sel;
1767 if (selection==NULL)
1771 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1772 while (gtk_tree_model_iter_next (model,&iter)) {
1773 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1774 if (strcmp(text, OK_TEXT) != 0) {
1775 gtk_tree_selection_select_iter (selection, &iter);
1776 path = gtk_tree_model_get_path(model, &iter);
1777 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1780 gtk_tree_path_free(path);
1787 if (user_data->dlg.number_of_nok>1){
1788 /* Get the first iter and select it before starting over */
1789 gtk_tree_model_get_iter_first(model, &iter);
1790 gtk_tree_selection_select_iter (selection, &iter);
1796 /****************************************************************************/
1797 /* when we want to save the information */
1798 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1801 GtkWidget *rev, *forw, *both;
1802 user_data_t *user_data;
1804 GtkListStore *store;
1806 GtkTreeModel *model;
1807 gboolean more_items = TRUE;
1809 /* To Hold data from the list row */
1810 guint packet; /* Packet */
1811 gfloat delta; /* Delta(ms) */
1812 gfloat jitter; /* Jitter(ms) */
1813 gfloat ipbw; /* IP BW(kbps) */
1814 char *status_str; /* Status */
1815 char *date_str; /* Date */
1816 guint length; /* Length */
1821 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1823 /* Perhaps the user specified a directory instead of a file.
1824 * Check whether they did.
1826 if (test_for_directory(g_dest) == EISDIR) {
1827 /* It's a directory - set the file selection box to display it. */
1828 set_last_open_dir(g_dest);
1829 file_selection_set_current_folder(fc, get_last_open_dir());
1830 gtk_file_chooser_set_current_name(fc, "");
1832 return FALSE; /* run the dialog again */
1834 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1835 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1836 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1837 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1839 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1840 fp = ws_fopen(g_dest, "w");
1842 open_failure_alert_box(g_dest, errno, TRUE);
1844 return TRUE; /* we're done */
1847 if (GTK_TOGGLE_BUTTON(both)->active) {
1848 fprintf(fp, "Forward\n");
1850 write_failure_alert_box(g_dest, errno);
1853 return TRUE; /* we're done */
1857 for(j = 0; j < NUM_COLS; j++) {
1859 fprintf(fp,"%s",titles[j]);
1861 fprintf(fp,",%s",titles[j]);
1866 write_failure_alert_box(g_dest, errno);
1869 return TRUE; /* we're done */
1871 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1872 store = GTK_LIST_STORE(model);
1873 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1875 while (more_items) {
1876 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1885 fprintf(fp, "%u",packet);
1886 fprintf(fp, ",%.2f", delta);
1887 fprintf(fp, ",%.2f", jitter);
1888 fprintf(fp, ",%.2f", ipbw);
1889 fprintf(fp, ",%s", status_str);
1890 fprintf(fp, ",%s", date_str);
1891 fprintf(fp, ",%u", length);
1896 write_failure_alert_box(g_dest, errno);
1899 return TRUE; /* we're done */
1902 more_items = gtk_tree_model_iter_next (model,&iter);
1906 if (fclose(fp) == EOF) {
1907 write_failure_alert_box(g_dest, errno);
1909 return TRUE; /* we're done */
1913 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1915 if (GTK_TOGGLE_BUTTON(both)->active) {
1916 fp = ws_fopen(g_dest, "a");
1918 open_failure_alert_box(g_dest, errno, TRUE);
1920 return TRUE; /* we're done */
1922 fprintf(fp, "\nReverse\n");
1924 write_failure_alert_box(g_dest, errno);
1927 return TRUE; /* we're done */
1930 fp = ws_fopen(g_dest, "w");
1932 open_failure_alert_box(g_dest, errno, TRUE);
1934 return TRUE; /* we're done */
1937 for(j = 0; j < NUM_COLS; j++) {
1939 fprintf(fp,"%s",titles[j]);
1941 fprintf(fp,",%s",titles[j]);
1946 write_failure_alert_box(g_dest, errno);
1949 return TRUE; /* we're done */
1951 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
1952 store = GTK_LIST_STORE(model);
1953 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1957 while (more_items) {
1958 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1967 fprintf(fp, "%u",packet);
1968 fprintf(fp, ",%.2f", delta);
1969 fprintf(fp, ",%.2f", jitter);
1970 fprintf(fp, ",%.2f", ipbw);
1971 fprintf(fp, ",%s", status_str);
1972 fprintf(fp, ",%s", date_str);
1973 fprintf(fp, ",%u", length);
1978 write_failure_alert_box(g_dest, errno);
1981 return TRUE; /* we're done */
1984 more_items = gtk_tree_model_iter_next (model,&iter);
1987 if (fclose(fp) == EOF) {
1988 write_failure_alert_box(g_dest, errno);
1990 return TRUE; /* we're done */
1994 return TRUE; /* we're done */
1997 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
1999 user_data->dlg.save_csv_as_w = NULL;
2002 /* when the user wants to save the csv information in a file */
2003 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2007 GtkWidget *label_format;
2008 GtkWidget *channels_label;
2009 GSList *channels_group = NULL;
2010 GtkWidget *forward_rb;
2011 GtkWidget *reversed_rb;
2014 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2015 if (user_data->dlg.save_csv_as_w != NULL) {
2016 /* There's already a Save CSV info dialog box; reactivate it. */
2017 reactivate_window(user_data->dlg.save_csv_as_w);
2021 user_data->dlg.save_csv_as_w =
2022 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2023 GTK_WINDOW(user_data->dlg.notebook),
2024 GTK_FILE_CHOOSER_ACTION_SAVE,
2025 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2026 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2028 #if GTK_CHECK_VERSION(2,8,0)
2029 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2031 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2033 /* Build our "extra widget" to be added to the file chooser widget */
2034 /* Container for each row of widgets */
2035 vertb = gtk_vbox_new(FALSE, 0);
2036 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2037 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2038 gtk_widget_show (vertb);
2040 table1 = gtk_table_new (2, 4, FALSE);
2041 gtk_widget_show (table1);
2042 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2043 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2044 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2046 label_format = gtk_label_new ("Format: Comma Separated Values");
2047 gtk_widget_show (label_format);
2048 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2049 (GtkAttachOptions) (GTK_FILL),
2050 (GtkAttachOptions) (0), 0, 0);
2051 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2053 channels_label = gtk_label_new ("Channels: ");
2054 gtk_widget_show (channels_label);
2055 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2056 (GtkAttachOptions) (GTK_FILL),
2057 (GtkAttachOptions) (0), 0, 0);
2058 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2060 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2061 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2062 gtk_widget_show (forward_rb);
2063 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2064 (GtkAttachOptions) (GTK_FILL),
2065 (GtkAttachOptions) (0), 0, 0);
2067 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2068 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2069 gtk_widget_show (reversed_rb);
2070 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2071 (GtkAttachOptions) (GTK_FILL),
2072 (GtkAttachOptions) (0), 0, 0);
2074 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2075 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2076 gtk_widget_show (both_rb);
2077 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2078 (GtkAttachOptions) (GTK_FILL),
2079 (GtkAttachOptions) (0), 0, 0);
2081 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2083 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2084 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2085 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2086 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2088 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2089 G_CALLBACK(window_delete_event_cb), NULL);
2090 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2091 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2093 gtk_widget_show(user_data->dlg.save_csv_as_w);
2094 window_present(user_data->dlg.save_csv_as_w);
2096 /* "Run" the GtkFileChooserDialog. */
2097 /* Upon exit: If "Accept" run the OK callback. */
2098 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2099 /* Destroy the window. */
2100 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2101 /* return with a TRUE status so that the dialog window will be destroyed. */
2102 /* Trying to re-run the dialog after popping up an alert box will not work */
2103 /* since the user will not be able to dismiss the alert box. */
2104 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2105 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2107 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2108 /* GtkFileChooserDialog. */
2109 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2110 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2111 break; /* we're done */
2114 window_destroy(user_data->dlg.save_csv_as_w);
2117 /****************************************************************************/
2118 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2120 /* Note that we no longer have a Save voice info dialog box. */
2121 user_data->dlg.save_voice_as_w = NULL;
2124 /****************************************************************************/
2125 /* here we save it into a file that user specified */
2126 /* XXX what about endians here? could go something wrong? */
2127 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2129 int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
2130 gchar f_pd[1] = {0};
2131 gchar r_pd[1] = {0};
2134 guint32 f_write_silence = 0;
2135 guint32 r_write_silence = 0;
2137 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2138 gboolean stop_flag = FALSE;
2141 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2144 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2150 /* open file for saving */
2151 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2158 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2160 if (format == SAVE_AU_FORMAT) /* au format */
2162 /* First we write the .au header. XXX Hope this is endian independant */
2163 /* the magic word 0x2e736e64 == .snd */
2164 phtonl(pd, 0x2e736e64);
2165 nchars=ws_write(to_fd, pd, 4);
2166 /* header offset == 24 bytes */
2168 nchars=ws_write(to_fd, pd, 4);
2169 /* total length, it is permited to set this to 0xffffffff */
2171 nchars=ws_write(to_fd, pd, 4);
2172 /* encoding format == 16-bit linear PCM */
2174 nchars=ws_write(to_fd, pd, 4);
2175 /* sample rate == 8000 Hz */
2177 nchars=ws_write(to_fd, pd, 4);
2180 nchars=ws_write(to_fd, pd, 4);
2184 /* only forward direction */
2185 case SAVE_FORWARD_DIRECTION_MASK: {
2186 progbar_count = user_data->forward.saveinfo.count;
2187 progbar_quantum = user_data->forward.saveinfo.count/100;
2188 while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
2191 if((count > progbar_nextstep) && (count <= progbar_count)) {
2192 update_progress_dlg(progbar,
2193 (gfloat) count/progbar_count, "Saving");
2194 progbar_nextstep = progbar_nextstep + progbar_quantum;
2198 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2199 sample = ulaw2linear(*f_pd);
2202 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2203 sample = alaw2linear(*f_pd);
2210 destroy_progress_dlg(progbar);
2214 fwritten = ws_write(to_fd, pd, 2);
2215 if ((fwritten < 2) || (fwritten < 0) || (fread_cnt < 0)) {
2219 destroy_progress_dlg(progbar);
2225 /* only reversed direction */
2226 case SAVE_REVERSE_DIRECTION_MASK: {
2227 progbar_count = user_data->reversed.saveinfo.count;
2228 progbar_quantum = user_data->reversed.saveinfo.count/100;
2229 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2232 if((count > progbar_nextstep) && (count <= progbar_count)) {
2233 update_progress_dlg(progbar,
2234 (gfloat) count/progbar_count, "Saving");
2235 progbar_nextstep = progbar_nextstep + progbar_quantum;
2239 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2240 sample = ulaw2linear(*r_pd);
2243 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2244 sample = alaw2linear(*r_pd);
2251 destroy_progress_dlg(progbar);
2255 rwritten = ws_write(to_fd, pd, 2);
2256 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2260 destroy_progress_dlg(progbar);
2266 /* both directions */
2267 case SAVE_BOTH_DIRECTION_MASK: {
2268 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2269 (progbar_count = user_data->forward.saveinfo.count) :
2270 (progbar_count = user_data->reversed.saveinfo.count);
2271 progbar_quantum = progbar_count/100;
2272 /* since conversation in one way can start later than in the other one,
2273 * we have to write some silence information for one channel */
2274 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2275 f_write_silence = (guint32)
2276 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2278 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2279 r_write_silence = (guint32)
2280 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2285 if((count > progbar_nextstep) && (count <= progbar_count)) {
2286 update_progress_dlg(progbar,
2287 (gfloat) count/progbar_count, "Saving");
2288 progbar_nextstep = progbar_nextstep + progbar_quantum;
2291 if(f_write_silence > 0) {
2292 rread = read(rev_fd, r_pd, 1);
2293 switch (user_data->forward.statinfo.reg_pt) {
2294 case AST_FORMAT_ULAW:
2295 *f_pd = SILENCE_PCMU;
2297 case AST_FORMAT_ALAW:
2298 *f_pd = SILENCE_PCMA;
2304 else if(r_write_silence > 0) {
2305 fread_cnt = read(forw_fd, f_pd, 1);
2306 switch (user_data->reversed.statinfo.reg_pt) {
2307 case AST_FORMAT_ULAW:
2308 *r_pd = SILENCE_PCMU;
2310 case AST_FORMAT_ALAW:
2311 *r_pd = SILENCE_PCMA;
2318 fread_cnt = read(forw_fd, f_pd, 1);
2319 rread = read(rev_fd, r_pd, 1);
2321 if ((rread == 0) && (fread_cnt == 0))
2323 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2324 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2327 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2328 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2336 destroy_progress_dlg(progbar);
2341 rwritten = ws_write(to_fd, pd, 2);
2342 if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
2346 destroy_progress_dlg(progbar);
2353 else if (format == SAVE_RAW_FORMAT) /* raw format */
2357 /* only forward direction */
2358 case SAVE_FORWARD_DIRECTION_MASK: {
2359 progbar_count = user_data->forward.saveinfo.count;
2360 progbar_quantum = user_data->forward.saveinfo.count/100;
2364 /* only reversed direction */
2365 case SAVE_REVERSE_DIRECTION_MASK: {
2366 progbar_count = user_data->reversed.saveinfo.count;
2367 progbar_quantum = user_data->reversed.saveinfo.count/100;
2375 destroy_progress_dlg(progbar);
2382 /* XXX how do you just copy the file? */
2383 while ((rread = read(fd, pd, 1)) > 0) {
2386 if((count > progbar_nextstep) && (count <= progbar_count)) {
2387 update_progress_dlg(progbar,
2388 (gfloat) count/progbar_count, "Saving");
2389 progbar_nextstep = progbar_nextstep + progbar_quantum;
2393 rwritten = ws_write(to_fd, pd, 1);
2395 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2399 destroy_progress_dlg(progbar);
2405 destroy_progress_dlg(progbar);
2413 /****************************************************************************/
2414 /* the user wants to save in a file */
2415 /* XXX support for different formats is currently commented out */
2416 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2419 /*GtkWidget *wav, *sw;*/
2420 GtkWidget *au, *raw;
2421 GtkWidget *rev, *forw, *both;
2422 user_data_t *user_data;
2423 gint channels , format;
2425 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2427 /* Perhaps the user specified a directory instead of a file.
2428 Check whether they did. */
2429 if (test_for_directory(g_dest) == EISDIR) {
2430 /* It's a directory - set the file selection box to display it. */
2431 set_last_open_dir(g_dest);
2432 file_selection_set_current_folder(fc, get_last_open_dir());
2433 gtk_file_chooser_set_current_name(fc, "");
2435 return FALSE; /* run the dialog again */
2439 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2440 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2442 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2443 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2444 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2445 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2446 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2447 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2449 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2450 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2451 * disable the ok button or disable the buttons for direction if only one is not ok. The
2452 * problem is if we open the save voice dialog and then click the refresh button and maybe
2453 * the state changes, so we can't save anymore. In this case we should be able to update
2454 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2457 /* we can not save in both directions */
2458 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2459 /* there are many combinations here, we just exit when first matches */
2460 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2461 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2462 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2463 "Can't save in a file: Unsupported codec!");
2464 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2465 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2466 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2467 "Can't save in a file: Wrong length of captured packets!");
2468 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2469 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2470 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2471 "Can't save in a file: Not all data in all packets was captured!");
2473 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2474 "Can't save in a file: File I/O problem!");
2476 return TRUE; /* we're done */
2478 /* we can not save forward direction */
2479 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2480 (GTK_TOGGLE_BUTTON (both)->active))) {
2481 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2482 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2483 "Can't save forward direction in a file: Unsupported codec!");
2484 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2485 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2486 "Can't save forward direction in a file: Wrong length of captured packets!");
2487 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2488 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2489 "Can't save forward direction in a file: Not all data in all packets was captured!");
2491 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2492 "Can't save forward direction in a file: File I/O problem!");
2494 return TRUE; /* we're done */
2496 /* we can not save reversed direction */
2497 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2498 (GTK_TOGGLE_BUTTON (both)->active))) {
2499 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2500 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2501 "Can't save reversed direction in a file: Unsupported codec!");
2502 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2503 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2504 "Can't save reversed direction in a file: Wrong length of captured packets!");
2505 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2506 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2507 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2508 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2509 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2510 "Can't save reversed direction in a file: No IAX2 data!");
2512 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2513 "Can't save reversed direction in a file: File I/O problem!");
2515 return TRUE; /* we're done */
2519 if (GTK_TOGGLE_BUTTON (wav)->active)
2520 format = SAVE_WAV_FORMAT;
2523 if (GTK_TOGGLE_BUTTON (au)->active)
2524 format = SAVE_AU_FORMAT;
2526 else if (GTK_TOGGLE_BUTTON (sw)->active)
2527 format = SAVE_SW_FORMAT;
2529 else if (GTK_TOGGLE_BUTTON (raw)->active)
2530 format = SAVE_RAW_FORMAT;
2532 format = SAVE_NONE_FORMAT;
2534 if (GTK_TOGGLE_BUTTON (rev)->active)
2535 channels = SAVE_REVERSE_DIRECTION_MASK;
2536 else if (GTK_TOGGLE_BUTTON (both)->active)
2537 channels = SAVE_BOTH_DIRECTION_MASK;
2539 channels = SAVE_FORWARD_DIRECTION_MASK;
2541 /* direction/format validity*/
2542 if (format == SAVE_AU_FORMAT)
2544 /* make sure streams are alaw/ulaw */
2545 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2546 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2547 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2549 return TRUE; /* we're done */
2551 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2552 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2553 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2555 return TRUE; /* we're done */
2557 /* make sure pt's don't differ */
2558 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2559 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2560 "Can't save in a file: Forward and reverse direction differ in type");
2562 return TRUE; /* we're done */
2565 else if (format == SAVE_RAW_FORMAT)
2567 /* can't save raw in both directions */
2568 if (channels == SAVE_BOTH_DIRECTION_MASK){
2569 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2570 "Can't save in a file: Unable to save raw data in both directions");
2572 return TRUE; /* we're done */
2577 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2578 "Can't save in a file: Invalid save format");
2580 return TRUE; /* we're done */
2583 if(!copy_file(g_dest, channels, format, user_data)) {
2584 /* XXX - report the error type! */
2585 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2586 "An error occurred while saving voice in a file!");
2588 return TRUE; /* we're done */
2591 return TRUE; /* we're done */
2594 /****************************************************************************/
2595 /* when the user wants to save the voice information in a file */
2596 /* XXX support for different formats is currently commented out */
2597 static void save_voice_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2601 GtkWidget *label_format;
2602 GtkWidget *channels_label;
2603 GSList *format_group = NULL;
2604 GSList *channels_group = NULL;
2605 GtkWidget *forward_rb;
2606 GtkWidget *reversed_rb;
2608 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2612 /* if we can't save in a file: wrong codec, cut packets or other errors */
2613 /* should the error arise here or later when you click ok button ?
2614 * if we do it here, then we must disable the refresh button, so we don't do it here */
2616 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2617 if (user_data->dlg.save_voice_as_w != NULL) {
2618 /* There's already a Save voice info dialog box; reactivate it. */
2619 reactivate_window(user_data->dlg.save_voice_as_w);
2623 /* XXX - use file_selection from dlg_utils instead! */
2624 user_data->dlg.save_voice_as_w =
2625 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2626 GTK_WINDOW(user_data->dlg.notebook),
2627 GTK_FILE_CHOOSER_ACTION_SAVE,
2628 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2629 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2631 #if GTK_CHECK_VERSION(2,8,0)
2632 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2634 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2636 /* Container for each row of widgets */
2637 vertb = gtk_vbox_new(FALSE, 0);
2638 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2639 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2640 gtk_widget_show (vertb);
2642 table1 = gtk_table_new (2, 4, FALSE);
2643 gtk_widget_show (table1);
2644 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2645 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2646 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2649 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2650 gtk_widget_show (label_format);
2651 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2652 (GtkAttachOptions) (GTK_FILL),
2653 (GtkAttachOptions) (0), 0, 0);
2656 label_format = gtk_label_new ("Format: ");
2657 gtk_widget_show (label_format);
2658 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2659 (GtkAttachOptions) (GTK_FILL),
2660 (GtkAttachOptions) (0), 0, 0);
2662 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2664 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2665 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2666 gtk_widget_show (raw_rb);
2667 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2668 (GtkAttachOptions) (GTK_FILL),
2669 (GtkAttachOptions) (0), 0, 0);
2672 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2673 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2674 gtk_widget_show (au_rb);
2675 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2676 (GtkAttachOptions) (GTK_FILL),
2677 (GtkAttachOptions) (0), 0, 0);
2680 /* we support .au - ulaw*/
2681 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2682 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2683 gtk_widget_show (wav_rb);
2684 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2685 (GtkAttachOptions) (GTK_FILL),
2686 (GtkAttachOptions) (0), 0, 0);
2688 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2689 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2690 gtk_widget_show (sw_rb);
2691 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2692 (GtkAttachOptions) (GTK_FILL),
2693 (GtkAttachOptions) (0), 0, 0);
2694 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2695 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2696 gtk_widget_show (au_rb);
2697 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2698 (GtkAttachOptions) (GTK_FILL),
2699 (GtkAttachOptions) (0), 0, 0);
2702 channels_label = gtk_label_new ("Channels: ");
2703 gtk_widget_show (channels_label);
2704 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2705 (GtkAttachOptions) (GTK_FILL),
2706 (GtkAttachOptions) (0), 0, 0);
2707 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2709 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2710 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2711 gtk_widget_show (forward_rb);
2712 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2713 (GtkAttachOptions) (GTK_FILL),
2714 (GtkAttachOptions) (0), 0, 0);
2716 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2717 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2718 gtk_widget_show (reversed_rb);
2719 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2720 (GtkAttachOptions) (GTK_FILL),
2721 (GtkAttachOptions) (0), 0, 0);
2723 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2724 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2725 gtk_widget_show (both_rb);
2726 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2727 (GtkAttachOptions) (GTK_FILL),
2728 (GtkAttachOptions) (0), 0, 0);
2730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2733 /* if one direction is nok we don't allow saving
2734 XXX this is not ok since the user can click the refresh button and cause changes
2735 but we can not update this window. So we move all the decision on the time the ok
2736 button is clicked */
2737 if (user_data->forward.saved == FALSE) {
2738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2739 gtk_widget_set_sensitive(forward_rb, FALSE);
2740 gtk_widget_set_sensitive(both_rb, FALSE);
2742 else if (user_data->reversed.saved == FALSE) {
2743 gtk_widget_set_sensitive(reversed_rb, FALSE);
2744 gtk_widget_set_sensitive(both_rb, FALSE);
2748 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2749 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2750 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2751 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2752 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2753 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2754 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2755 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2757 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2758 G_CALLBACK(window_delete_event_cb), NULL);
2759 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2760 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2762 gtk_widget_show(user_data->dlg.save_voice_as_w);
2763 window_present(user_data->dlg.save_voice_as_w);
2765 /* "Run" the GtkFileChooserDialog. */
2766 /* Upon exit: If "Accept" run the OK callback. */
2767 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2768 /* Destroy the window. */
2769 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2770 /* return with a TRUE status so that the dialog window will be destroyed. */
2771 /* Trying to re-run the dialog after popping up an alert box will not work */
2772 /* since the user will not be able to dismiss the alert box. */
2773 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2774 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2776 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2777 /* GtkFileChooserDialog. */
2778 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2779 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2780 break; /* we're done */
2783 window_destroy(user_data->dlg.save_voice_as_w);
2787 /****************************************************************************/
2788 /* when we are finished with redisection, we add the label for the statistic */
2789 static void draw_stat(user_data_t *user_data)
2791 gchar label_max[200];
2793 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2794 user_data->forward.statinfo.total_nr,
2795 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2797 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2799 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2800 user_data->reversed.statinfo.total_nr,
2801 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2803 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2810 /****************************************************************************/
2811 /* append a line to list */
2812 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2813 double delta, double jitter, double bandwidth, gchar *status,
2814 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2816 GtkListStore *list_store;
2818 if (strcmp(status, OK_TEXT) != 0) {
2819 user_data->dlg.number_of_nok++;
2822 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2824 /* Creates a new row at position. iter will be changed to point to this new row.
2825 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2826 * The row will be filled with the values given to this function.
2828 * should generally be preferred when inserting rows in a sorted list store.
2830 #if GTK_CHECK_VERSION(2,6,0)
2831 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2832 PACKET_COLUMN, number,
2833 DELTA_COLUMN, delta,
2834 JITTER_COLUMN, jitter,
2835 IPBW_COLUMN, bandwidth,
2836 STATUS_COLUMN, (char *)status,
2837 DATE_COLUMN, (char *)timeStr,
2838 LENGTH_COLUMN, pkt_len,
2839 FOREGROUND_COLOR_COL, NULL,
2840 BACKGROUND_COLOR_COL, (char *)color_str,
2844 gtk_list_store_append (list_store, &user_data->dlg.iter);
2845 gtk_list_store_set (list_store, &user_data->dlg.iter,
2846 PACKET_COLUMN, number,
2847 DELTA_COLUMN, delta,
2848 JITTER_COLUMN, jitter,
2849 IPBW_COLUMN, bandwidth,
2850 STATUS_COLUMN, (char *)status,
2851 DATE_COLUMN, (char *)timeStr,
2852 LENGTH_COLUMN, pkt_len,
2853 FOREGROUND_COLOR_COL, NULL,
2854 BACKGROUND_COLOR_COL, (char *)color_str,
2857 if(flags & STAT_FLAG_FIRST){
2858 /* Set first row as active */
2859 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2863 /****************************************************************************
2864 * Functions needed to present values from the list
2867 /* Present floats with two decimals */
2869 iax2_float_data_func (GtkTreeViewColumn *column _U_,
2870 GtkCellRenderer *renderer,
2871 GtkTreeModel *model,
2879 /* the col to get data from is in userdata */
2880 gint float_col = GPOINTER_TO_INT(user_data);
2882 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
2884 /* save the current locale */
2885 savelocale = setlocale(LC_NUMERIC, NULL);
2886 /* switch to "C" locale to avoid problems with localized decimal separators
2887 * in g_snprintf("%f") functions
2889 setlocale(LC_NUMERIC, "C");
2891 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
2892 /* restore previous locale setting */
2893 setlocale(LC_NUMERIC, savelocale);
2895 g_object_set(renderer, "text", buf, NULL);
2901 GtkWidget* create_list(user_data_t* user_data)
2904 GtkListStore *list_store;
2906 GtkTreeViewColumn *column;
2907 GtkCellRenderer *renderer;
2908 GtkTreeSortable *sortable;
2909 GtkTreeView *list_view;
2910 GtkTreeSelection *selection;
2912 /* Create the store */
2913 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2914 G_TYPE_UINT, /* Packet */
2915 G_TYPE_FLOAT, /* Delta(ms) */
2916 G_TYPE_FLOAT, /* Jitter(ms) */
2917 G_TYPE_FLOAT, /* IP BW(kbps) */
2918 G_TYPE_STRING, /* Status */
2919 G_TYPE_STRING, /* Date */
2920 G_TYPE_UINT, /* Length */
2921 G_TYPE_STRING, /* Foreground color */
2922 G_TYPE_STRING); /* Background color */
2925 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2927 list_view = GTK_TREE_VIEW(list);
2928 sortable = GTK_TREE_SORTABLE(list_store);
2930 #if GTK_CHECK_VERSION(2,6,0)
2931 /* Speed up the list display */
2932 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2935 /* Setup the sortable columns */
2936 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2937 gtk_tree_view_set_headers_clickable(list_view, FALSE);
2939 /* The view now holds a reference. We can get rid of our own reference */
2940 g_object_unref (G_OBJECT (list_store));
2943 * Create the first column packet, associating the "text" attribute of the
2944 * cell_renderer to the first column of the model
2946 renderer = gtk_cell_renderer_text_new ();
2947 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
2948 "text", PACKET_COLUMN,
2949 "foreground", FOREGROUND_COLOR_COL,
2950 "background", BACKGROUND_COLOR_COL,
2952 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2953 gtk_tree_view_column_set_resizable(column, TRUE);
2954 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2955 gtk_tree_view_column_set_min_width(column, 100);
2957 /* Add the column to the view. */
2958 gtk_tree_view_append_column (list_view, column);
2960 /* Second column.. Delta(ms). */
2961 renderer = gtk_cell_renderer_text_new ();
2962 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
2963 "text", DELTA_COLUMN,
2964 "foreground", FOREGROUND_COLOR_COL,
2965 "background", BACKGROUND_COLOR_COL,
2968 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2969 GINT_TO_POINTER(DELTA_COLUMN), NULL);
2971 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
2972 gtk_tree_view_column_set_resizable(column, TRUE);
2973 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2974 gtk_tree_view_column_set_min_width(column, 100);
2975 gtk_tree_view_append_column (list_view, column);
2977 /* Third column.. Jitter(ms). */
2978 renderer = gtk_cell_renderer_text_new ();
2979 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
2980 "text", JITTER_COLUMN,
2981 "foreground", FOREGROUND_COLOR_COL,
2982 "background", BACKGROUND_COLOR_COL,
2985 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2986 GINT_TO_POINTER(JITTER_COLUMN), NULL);
2988 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
2989 gtk_tree_view_column_set_resizable(column, TRUE);
2990 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2991 gtk_tree_view_column_set_min_width(column, 100);
2992 gtk_tree_view_append_column (list_view, column);
2994 /* Fourth column.. IP BW(kbps). */
2995 renderer = gtk_cell_renderer_text_new ();
2996 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
2997 "text", IPBW_COLUMN,
2998 "foreground", FOREGROUND_COLOR_COL,
2999 "background", BACKGROUND_COLOR_COL,
3002 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3003 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3005 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3006 gtk_tree_view_column_set_resizable(column, TRUE);
3007 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3008 gtk_tree_view_column_set_min_width(column, 100);
3009 gtk_tree_view_append_column (list_view, column);
3011 /* Fifth column.. Status. */
3012 renderer = gtk_cell_renderer_text_new ();
3013 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3014 "text", STATUS_COLUMN,
3015 "foreground", FOREGROUND_COLOR_COL,
3016 "background", BACKGROUND_COLOR_COL,
3018 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3019 gtk_tree_view_column_set_resizable(column, TRUE);
3020 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3021 gtk_tree_view_column_set_min_width(column, 100);
3022 gtk_tree_view_append_column (list_view, column);
3024 /* Sixth column.. Length. */
3025 renderer = gtk_cell_renderer_text_new ();
3026 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3027 "text", LENGTH_COLUMN,
3028 "foreground", FOREGROUND_COLOR_COL,
3029 "background", BACKGROUND_COLOR_COL,
3033 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3034 gtk_tree_view_column_set_resizable(column, TRUE);
3035 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3036 gtk_tree_view_column_set_min_width(column, 100);
3037 gtk_tree_view_append_column (list_view, column);
3039 /* Now enable the sorting of each column */
3040 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3041 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3043 /* Setup the selection handler */
3044 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3045 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3047 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3048 G_CALLBACK (on_list_select_row),
3055 /****************************************************************************/
3056 /* Create the dialog box with all widgets */
3057 static void create_iax2_dialog(user_data_t* user_data)
3059 GtkWidget *window = NULL;
3060 GtkWidget *list_fwd;
3061 GtkWidget *list_rev;
3062 GtkWidget *label_stats_fwd;
3063 GtkWidget *label_stats_rev;
3064 GtkWidget *notebook;
3066 GtkWidget *main_vb, *page, *page_r;
3068 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3069 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3070 GtkWidget *graph_bt;
3071 gchar label_forward[150];
3072 gchar label_reverse[150];
3074 gchar str_ip_src[16];
3075 gchar str_ip_dst[16];
3077 window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
3078 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3080 /* Container for each row of widgets */
3081 main_vb = gtk_vbox_new(FALSE, 2);
3082 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3083 gtk_container_add(GTK_CONTAINER(window), main_vb);
3084 gtk_widget_show(main_vb);
3087 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3088 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3090 g_snprintf(label_forward, sizeof(label_forward),
3091 "Analysing stream from %s port %u to %s port %u ",
3092 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3095 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3096 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3098 g_snprintf(label_reverse, sizeof(label_reverse),
3099 "Analysing stream from %s port %u to %s port %u ",
3100 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3102 /* Start a notebook for flipping between sets of changes */
3103 notebook = gtk_notebook_new();
3104 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3105 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3107 user_data->dlg.notebook_signal_id =
3108 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3110 /* page for forward connection */
3111 page = gtk_vbox_new(FALSE, 8);
3112 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3114 /* direction label */
3115 label = gtk_label_new(label_forward);
3116 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3118 /* place for some statistics */
3119 label_stats_fwd = gtk_label_new("\n");
3120 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3122 /* scrolled window */
3123 scrolled_window = scrolled_window_new(NULL, NULL);
3126 list_fwd = create_list(user_data);
3127 gtk_widget_show(list_fwd);
3128 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3129 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3130 gtk_widget_show(scrolled_window);
3133 label = gtk_label_new(" Forward Direction ");
3134 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3136 /* same page for reversed connection */
3137 page_r = gtk_vbox_new(FALSE, 8);
3138 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3139 label = gtk_label_new(label_reverse);
3140 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3141 label_stats_rev = gtk_label_new("\n");
3142 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3144 scrolled_window_r = scrolled_window_new(NULL, NULL);
3146 list_rev = create_list(user_data);
3147 gtk_widget_show(list_rev);
3148 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3149 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3150 gtk_widget_show(scrolled_window_r);
3152 label = gtk_label_new(" Reversed Direction ");
3153 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3156 /* page for help&about or future */
3157 page_help = gtk_hbox_new(FALSE, 5);
3158 label = gtk_label_new(" Future ");
3159 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3160 frame = gtk_frame_new("");
3161 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3162 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3163 gtk_container_add(GTK_CONTAINER(frame), text);
3164 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3165 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3168 /* show all notebooks */
3169 gtk_widget_show_all(notebook);
3172 box4 = gtk_hbutton_box_new();
3173 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3174 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3175 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3176 gtk_box_set_spacing(GTK_BOX (box4), 0);
3177 gtk_widget_show(box4);
3179 voice_bt = gtk_button_new_with_label("Save payload...");
3180 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3181 gtk_widget_show(voice_bt);
3182 g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3184 csv_bt = gtk_button_new_with_label("Save as CSV...");
3185 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3186 gtk_widget_show(csv_bt);
3187 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3189 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3190 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3191 gtk_widget_show(refresh_bt);
3192 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3194 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3195 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3196 gtk_widget_show(goto_bt);
3197 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3199 graph_bt = gtk_button_new_with_label("Graph");
3200 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3201 gtk_widget_show(graph_bt);
3202 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3204 next_bt = gtk_button_new_with_label("Next non-Ok");
3205 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3206 gtk_widget_show(next_bt);
3207 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3209 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3210 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3211 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3212 gtk_widget_show(close_bt);
3213 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3215 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3216 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3218 gtk_widget_show(window);
3219 window_present(window);
3221 /* some widget references need to be saved for outside use */
3222 user_data->dlg.window = window;
3223 user_data->dlg.list_fwd = list_fwd;
3224 user_data->dlg.list_rev = list_rev;
3225 user_data->dlg.label_stats_fwd = label_stats_fwd;
3226 user_data->dlg.label_stats_rev = label_stats_rev;
3227 user_data->dlg.notebook = notebook;
3228 user_data->dlg.selected_list = list_fwd;
3229 user_data->dlg.number_of_nok = 0;
3232 * select the initial row
3234 gtk_widget_grab_focus(list_fwd);
3238 /****************************************************************************/
3239 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3240 const gchar* proto_field, guint32* p_result)
3243 proto_node *proto_sibling_node;
3244 header_field_info *hfssrc;
3247 finfo = PNODE_FINFO(ptree_node);
3249 if (hfinformation==(finfo->hfinfo)) {
3250 hfssrc = proto_registrar_get_byname(proto_field);
3251 if (hfssrc == NULL) {
3252 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3256 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3257 ptree_node=ptree_node->next) {
3258 finfo=PNODE_FINFO(ptree_node);
3259 if (hfssrc==finfo->hfinfo) {
3260 if (hfinformation->type==FT_IPv4) {
3261 ipv4 = fvalue_get(&finfo->value);
3262 *p_result = ipv4_get_net_order_addr(ipv4);
3265 *p_result = fvalue_get_uinteger(&finfo->value);
3274 proto_sibling_node = ptree_node->next;
3276 if (proto_sibling_node) {
3277 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3283 /****************************************************************************/
3284 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3285 const gchar* proto_name,
3286 const gchar* proto_field,
3289 proto_node *ptree_node;
3290 header_field_info *hfinformation;
3292 hfinformation = proto_registrar_get_byname(proto_name);
3293 if (hfinformation == NULL) {
3294 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3299 ptree_node = ((proto_node *)protocol_tree)->first_child;
3301 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3305 return process_node(ptree_node, hfinformation, proto_field, p_result);
3309 /****************************************************************************/
3311 address *ip_src_fwd,
3312 guint16 port_src_fwd,
3313 address *ip_dst_fwd,
3314 guint16 port_dst_fwd,
3315 address *ip_src_rev,
3316 guint16 port_src_rev,
3317 address *ip_dst_rev,
3318 guint16 port_dst_rev
3321 user_data_t *user_data;
3324 static color_t col[MAX_GRAPHS] = {
3325 {0, 0x0000, 0x0000, 0x0000},
3326 {0, 0xffff, 0x0000, 0x0000},
3327 {0, 0x0000, 0xffff, 0x0000},
3328 {0, 0x0000, 0x0000, 0xffff}
3333 user_data = g_malloc(sizeof(user_data_t));
3335 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3336 user_data->port_src_fwd = port_src_fwd;
3337 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3338 user_data->port_dst_fwd = port_dst_fwd;
3339 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3340 user_data->port_src_rev = port_src_rev;
3341 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3342 user_data->port_dst_rev = port_dst_rev;
3345 /* file names for storing sound data */
3346 /*XXX: check for errors*/
3347 fd = create_tempfile(&tempname, "wireshark_iax2_f");
3348 user_data->f_tempname = g_strdup(tempname);
3350 fd = create_tempfile(&tempname, "wireshark_iax2_r");
3351 user_data->r_tempname = g_strdup(tempname);
3353 user_data->forward.saveinfo.fp = NULL;
3354 user_data->reversed.saveinfo.fp = NULL;
3355 user_data->dlg.save_voice_as_w = NULL;
3356 user_data->dlg.save_csv_as_w = NULL;
3357 user_data->dlg.dialog_graph.window = NULL;
3359 /* init dialog_graph */
3360 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3361 user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
3362 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
3363 user_data->dlg.dialog_graph.draw_area=NULL;
3364 user_data->dlg.dialog_graph.pixmap=NULL;
3365 user_data->dlg.dialog_graph.scrollbar=NULL;
3366 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3367 user_data->dlg.dialog_graph.pixmap_width=500;
3368 user_data->dlg.dialog_graph.pixmap_height=200;
3369 user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
3370 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3371 user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
3372 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3373 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3374 user_data->dlg.dialog_graph.max_interval=0;
3375 user_data->dlg.dialog_graph.num_items=0;
3376 user_data->dlg.dialog_graph.start_time = -1;
3378 for(i=0;i<MAX_GRAPHS;i++){
3379 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3380 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3381 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3382 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3383 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3384 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3385 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3386 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3389 /* create the dialog box */
3390 create_iax2_dialog(user_data);
3392 /* proceed as if the Refresh button would have been pressed */
3393 on_refresh_bt_clicked(NULL, user_data);
3396 /****************************************************************************/
3397 /* entry point from main menu */
3398 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3401 guint16 port_src_fwd;
3403 guint16 port_dst_fwd;
3405 guint16 port_src_rev;
3407 guint16 port_dst_rev;
3408 /* unsigned int ptype; */
3410 gchar filter_text[256];
3414 gboolean frame_matched;
3416 GList *strinfo_list;
3417 GList *filtered_list = NULL;
3418 rtp_stream_info_t *strinfo;
3421 /* Try to compile the filter. */
3422 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3423 if (!dfilter_compile(filter_text, &sfcode)) {
3424 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3427 /* we load the current file into cf variable */
3429 fdata = cf->current_frame;
3431 /* we are on the selected frame now */
3433 return; /* if we exit here it's an error */
3435 /* dissect the current frame */
3436 if (!cf_read_frame(cf, fdata))
3437 return; /* error reading the frame */
3438 epan_dissect_init(&edt, TRUE, FALSE);
3439 epan_dissect_prime_dfilter(&edt, sfcode);
3440 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3441 frame_matched = dfilter_apply_edt(sfcode, &edt);
3443 /* if it is not an iax2 frame, show an error dialog */
3444 frame_matched = dfilter_apply_edt(sfcode, &edt);
3445 if (frame_matched != 1) {
3446 epan_dissect_cleanup(&edt);
3447 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3448 "You didn't choose a IAX2 packet!");
3451 /* check if it is Voice or MiniPacket
3452 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3453 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3454 "Please select a Voice packet!");
3458 /* check if it is part of a Call */
3459 if (edt.pi.circuit_id == 0) {
3460 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3461 "Please select a Call packet!");
3465 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3466 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3467 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3468 port_src_fwd = edt.pi.srcport;
3469 port_dst_fwd = edt.pi.destport;
3471 /* assume the inverse ip/port combination for the reverse direction */
3472 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3473 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3474 port_src_rev = edt.pi.destport;
3475 port_dst_rev = edt.pi.srcport;
3477 /* Scan for rtpstream */
3479 /* search for reversed direction in the global rtp streams list */
3481 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3482 while (strinfo_list)
3484 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3485 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3486 && strinfo->src_port==port_src_fwd
3487 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3488 && strinfo->dest_port==port_dst_fwd)
3490 filtered_list = g_list_prepend(filtered_list, strinfo);
3493 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3494 && strinfo->src_port==port_src_rev
3495 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3496 && strinfo->dest_port==port_dst_rev)
3499 filtered_list = g_list_append(filtered_list, strinfo);
3502 strinfo_list = g_list_next(strinfo_list);
3505 /* if more than one reverse streams found, we let the user choose the right one */
3507 rtpstream_dlg_show(filtered_list);
3524 /****************************************************************************/
3526 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3528 iax2_analysis_cb(NULL, NULL);
3531 /****************************************************************************/
3533 register_tap_listener_iax2_analysis(void)
3535 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3537 register_stat_menu_item("IA_X2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3538 iax2_analysis_cb, NULL, NULL, NULL);
3548 * indent-tabs-mode: t
3551 * ex: set shiftwidth=8 tabstop=8 noexpandtab
3552 * :indentSize=8:tabSize=8:noTabs=false: