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.
54 #include <epan/epan_dissect.h>
55 #include <epan/filesystem.h>
56 #include <epan/pint.h>
58 #include <epan/tap-voip.h>
59 #include <epan/dissectors/packet-iax2.h>
60 #include <epan/iax2_codec_type.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
66 #include "../register.h"
68 #include "../alert_box.h"
69 #include "../simple_dialog.h"
70 #include "../stat_menu.h"
71 #include "../progress_dlg.h"
73 #include "../tempfile.h"
74 #include <wsutil/file_util.h>
76 #include "gtk/gtkglobals.h"
77 #include "gtk/dlg_utils.h"
78 #include "gtk/file_dlg.h"
79 #include "gtk/gui_utils.h"
80 #include "gtk/gui_stat_menu.h"
82 #include "gtk/rtp_analysis.h"
83 #include "gtk/iax2_analysis.h"
84 #include "gtk/rtp_stream.h"
85 #include "gtk/rtp_stream_dlg.h"
98 N_COLUMN /* The number of columns */
101 /****************************************************************************/
103 typedef struct column_arrows {
105 GtkWidget *ascend_pm;
106 GtkWidget *descend_pm;
110 #define NUM_GRAPH_ITEMS 100000
111 #define MAX_YSCALE 16
112 #define AUTO_MAX_YSCALE 0
114 #define GRAPH_FWD_JITTER 0
115 #define GRAPH_FWD_DIFF 1
116 #define GRAPH_REV_JITTER 2
117 #define GRAPH_REV_DIFF 3
118 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
120 #define MAX_PIXELS_PER_TICK 4
121 #define DEFAULT_PIXELS_PER_TICK 1
122 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
123 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
125 #define MAX_TICK_VALUES 5
126 #define DEFAULT_TICK_VALUE 1
127 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
128 typedef struct _dialog_graph_graph_item_t {
131 } dialog_graph_graph_item_t;
133 typedef struct _dialog_graph_graph_t {
134 struct _user_data_t *ud;
135 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
138 GtkWidget *display_button;
143 } dialog_graph_graph_t;
146 typedef struct _dialog_graph_t {
147 gboolean needs_redraw;
148 gint32 interval; /* measurement interval in ms */
149 guint32 last_interval;
150 guint32 max_interval; /* XXX max_interval and num_items are redundant */
152 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
154 GtkWidget *draw_area;
156 GtkAdjustment *scrollbar_adjustment;
157 GtkWidget *scrollbar;
165 typedef struct _dialog_data_t {
170 GtkWidget *label_stats_fwd;
171 GtkWidget *label_stats_rev;
172 GtkWidget *selected_list;
174 GtkTreeSelection *selected_list_sel;
175 gint selected_list_row;
177 GtkWidget *save_voice_as_w;
178 GtkWidget *save_csv_as_w;
179 gint notebook_signal_id;
180 dialog_graph_t dialog_graph;
181 #ifdef USE_CONVERSATION_GRAPH
182 GtkWidget *graph_window;
186 #define OK_TEXT "[ Ok ]"
188 /* type of error when saving voice in a file didn't succeed */
191 TAP_RTP_WRONG_LENGTH,
192 TAP_RTP_PADDING_ERROR,
194 TAP_RTP_FILE_OPEN_ERROR,
198 typedef struct _tap_iax2_save_info_t {
201 error_type_t error_type;
203 } tap_iax2_save_info_t;
206 /* structure that holds the information about the forward and reversed direction */
207 struct _info_direction {
208 tap_iax2_stat_t statinfo;
209 tap_iax2_save_info_t saveinfo;
212 #define TMPNAMSIZE 100
214 #define SILENCE_PCMU (guint8)0xFF
215 #define SILENCE_PCMA (guint8)0x55
217 /* structure that holds general information about the connection
218 * and structures for both directions */
219 typedef struct _user_data_t {
220 /* tap associated data*/
222 guint16 port_src_fwd;
224 guint16 port_dst_fwd;
226 guint16 port_src_rev;
228 guint16 port_dst_rev;
230 struct _info_direction forward;
231 struct _info_direction reversed;
233 char f_tempname[TMPNAMSIZE];
234 char r_tempname[TMPNAMSIZE];
236 /* dialog associated data */
239 #ifdef USE_CONVERSATION_GRAPH
240 time_series_t series_fwd;
241 time_series_t series_rev;
247 static const gchar *titles[7] = {
257 #define SAVE_FORWARD_DIRECTION_MASK 0x01
258 #define SAVE_REVERSE_DIRECTION_MASK 0x02
259 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
261 #define SAVE_NONE_FORMAT 0
262 #define SAVE_WAV_FORMAT 1
263 #define SAVE_AU_FORMAT 2
264 #define SAVE_SW_FORMAT 3
265 #define SAVE_RAW_FORMAT 4
268 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
269 /****************************************************************************/
270 static void enable_graph(dialog_graph_graph_t *dgg)
277 static void dialog_graph_reset(user_data_t* user_data);
281 /****************************************************************************/
284 /****************************************************************************/
285 /* when there is a [re]reading of packet's */
287 iax2_reset(void *user_data_arg)
289 user_data_t *user_data = user_data_arg;
290 user_data->forward.statinfo.first_packet = TRUE;
291 user_data->reversed.statinfo.first_packet = TRUE;
292 user_data->forward.statinfo.max_delta = 0;
293 user_data->reversed.statinfo.max_delta = 0;
294 user_data->forward.statinfo.max_jitter = 0;
295 user_data->reversed.statinfo.max_jitter = 0;
296 user_data->forward.statinfo.mean_jitter = 0;
297 user_data->reversed.statinfo.mean_jitter = 0;
298 user_data->forward.statinfo.delta = 0;
299 user_data->reversed.statinfo.delta = 0;
300 user_data->forward.statinfo.diff = 0;
301 user_data->reversed.statinfo.diff = 0;
302 user_data->forward.statinfo.jitter = 0;
303 user_data->reversed.statinfo.jitter = 0;
304 user_data->forward.statinfo.bandwidth = 0;
305 user_data->reversed.statinfo.bandwidth = 0;
306 user_data->forward.statinfo.total_bytes = 0;
307 user_data->reversed.statinfo.total_bytes = 0;
308 user_data->forward.statinfo.bw_start_index = 0;
309 user_data->reversed.statinfo.bw_start_index = 0;
310 user_data->forward.statinfo.bw_index = 0;
311 user_data->reversed.statinfo.bw_index = 0;
312 user_data->forward.statinfo.timestamp = 0;
313 user_data->reversed.statinfo.timestamp = 0;
314 user_data->forward.statinfo.max_nr = 0;
315 user_data->reversed.statinfo.max_nr = 0;
316 user_data->forward.statinfo.total_nr = 0;
317 user_data->reversed.statinfo.total_nr = 0;
318 user_data->forward.statinfo.sequence = 0;
319 user_data->reversed.statinfo.sequence = 0;
320 user_data->forward.statinfo.start_seq_nr = 0;
321 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
322 user_data->forward.statinfo.stop_seq_nr = 0;
323 user_data->reversed.statinfo.stop_seq_nr = 0;
324 user_data->forward.statinfo.cycles = 0;
325 user_data->reversed.statinfo.cycles = 0;
326 user_data->forward.statinfo.under = FALSE;
327 user_data->reversed.statinfo.under = FALSE;
328 user_data->forward.statinfo.start_time = 0;
329 user_data->reversed.statinfo.start_time = 0;
330 user_data->forward.statinfo.time = 0;
331 user_data->reversed.statinfo.time = 0;
332 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
333 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
335 user_data->forward.saveinfo.count = 0;
336 user_data->reversed.saveinfo.count = 0;
337 user_data->forward.saveinfo.saved = FALSE;
338 user_data->reversed.saveinfo.saved = FALSE;
340 /* clear the dialog box lists */
341 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
342 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
344 /* reset graph info */
345 dialog_graph_reset(user_data);
347 #ifdef USE_CONVERSATION_GRAPH
348 if (user_data->dlg.graph_window != NULL)
349 window_destroy(user_data->dlg.graph_window);
351 g_array_free(user_data->series_fwd.value_pairs, TRUE);
352 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
354 g_array_free(user_data->series_rev.value_pairs, TRUE);
355 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
358 /* XXX check for error at fclose? */
359 if (user_data->forward.saveinfo.fp != NULL)
360 fclose(user_data->forward.saveinfo.fp);
361 if (user_data->reversed.saveinfo.fp != NULL)
362 fclose(user_data->reversed.saveinfo.fp);
363 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
364 if (user_data->forward.saveinfo.fp == NULL)
365 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
366 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
367 if (user_data->reversed.saveinfo.fp == NULL)
368 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
372 /****************************************************************************/
373 static int iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
375 dialog_graph_graph_item_t *it;
379 /* we sometimes get called when dgg is disabled.
380 this is a bug since the tap listener should be removed first */
385 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
388 * Find which interval this is supposed to to in and store the
389 * interval index as idx
391 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
392 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
394 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
398 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
400 /* some sanity checks */
401 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
405 /* update num_items */
406 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
407 dgg->ud->dlg.dialog_graph.num_items=idx;
408 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
412 * Find the appropriate dialog_graph_graph_item_t structure
417 * Use the max value to highlight RTP problems
419 if (value > it->value) {
422 it->flags = it->flags | statinfo->flags;
427 /****************************************************************************/
428 /* here we can redraw the output */
430 static void iax2_draw(void *prs _U_)
435 /* forward declarations */
436 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
437 double delta, double jitter, double bandwidth, gchar *status,
438 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
440 static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
441 tap_iax2_stat_t *statinfo, packet_info *pinfo,
442 const struct _iax2_info_t *iax2info);
444 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
445 tap_iax2_stat_t *statinfo,
447 const struct _iax2_info_t *iax2info);
450 /****************************************************************************/
451 /* whenever a IAX2 packet is seen by the tap listener */
452 static int iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
454 user_data_t *user_data = user_data_arg;
455 const struct _iax2_info_t *iax2info = iax2info_arg;
456 #ifdef USE_CONVERSATION_GRAPH
459 /* we ignore packets that are not displayed */
460 if (pinfo->fd->flags.passed_dfilter == 0)
463 /* we ignore packets that carry no data */
464 if (iax2info->payload_len == 0)
467 /* is it the forward direction? */
468 else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
469 && user_data->port_src_fwd == pinfo->srcport
470 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
471 && user_data->port_dst_fwd == pinfo->destport) {
472 #ifdef USE_CONVERSATION_GRAPH
473 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
474 vp.fnumber = pinfo->fd->num;
475 g_array_append_val(user_data->series_fwd.value_pairs, vp);
477 iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
478 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));
479 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));
480 iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
481 &(user_data->forward.statinfo), pinfo, iax2info);
482 iax2_packet_save_payload(&(user_data->forward.saveinfo),
483 &(user_data->forward.statinfo), pinfo, iax2info);
485 /* is it the reversed direction? */
486 else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
487 && user_data->port_src_rev == pinfo->srcport
488 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
489 && user_data->port_dst_rev == pinfo->destport) {
490 #ifdef USE_CONVERSATION_GRAPH
491 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
492 vp.fnumber = pinfo->fd->num;
493 g_array_append_val(user_data->series_rev.value_pairs, vp);
495 iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
496 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));
497 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));
498 iax2_packet_add_info(user_data->dlg.list_rev, user_data,
499 &(user_data->reversed.statinfo), pinfo, iax2info);
500 iax2_packet_save_payload(&(user_data->reversed.saveinfo),
501 &(user_data->reversed.statinfo), pinfo, iax2info);
507 /****************************************************************************/
508 /* This comes from tap-rtp-common.c */
509 /****************************************************************************/
511 int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
513 const struct _iax2_info_t *iax2info)
516 double current_jitter;
520 /* check payload type */
521 if (iax2info->ftype == AST_FRAME_VOICE) {
522 if (iax2info->csub != statinfo->pt)
523 statinfo->flags |= STAT_FLAG_PT_CHANGE;
524 statinfo->pt = iax2info->csub;
527 /* store the current time and calculate the current jitter */
528 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
529 current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
530 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
531 statinfo->delta = current_time-(statinfo->time);
532 statinfo->jitter = current_jitter;
533 statinfo->diff = current_diff;
535 /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
536 statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
537 statinfo->bw_history[statinfo->bw_index].time = current_time;
538 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
539 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
540 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
541 statinfo->bw_start_index++;
542 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
544 statinfo->total_bytes += iax2info->payload_len + 24;
545 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
546 statinfo->bw_index++;
547 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
550 /* is this the first packet we got in this direction? */
551 if (statinfo->first_packet) {
552 statinfo->start_seq_nr = 0;
553 statinfo->start_time = current_time;
555 statinfo->jitter = 0;
557 statinfo->flags |= STAT_FLAG_FIRST;
558 statinfo->first_packet = FALSE;
560 /* is it a regular packet? */
561 if (!(statinfo->flags & STAT_FLAG_FIRST)
562 && !(statinfo->flags & STAT_FLAG_MARKER)
563 && !(statinfo->flags & STAT_FLAG_PT_CN)
564 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
565 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
566 /* include it in maximum delta calculation */
567 if (statinfo->delta > statinfo->max_delta) {
568 statinfo->max_delta = statinfo->delta;
569 statinfo->max_nr = pinfo->fd->num;
571 /* maximum and mean jitter calculation */
572 if (statinfo->jitter > statinfo->max_jitter) {
573 statinfo->max_jitter = statinfo->jitter;
575 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
577 /* regular payload change? (CN ignored) */
578 if (!(statinfo->flags & STAT_FLAG_FIRST)
579 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
580 if ((statinfo->pt != statinfo->reg_pt)
581 && (statinfo->reg_pt != PT_UNDEFINED)) {
582 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
586 /* set regular payload*/
587 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
588 statinfo->reg_pt = statinfo->pt;
591 /* TODO: lost packets / duplicated: we should infer this from timestamp... */
592 statinfo->time = current_time;
593 statinfo->timestamp = iax2info->timestamp;
594 statinfo->stop_seq_nr = 0;
595 statinfo->total_nr++;
601 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
602 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
603 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
604 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
605 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
607 /****************************************************************************/
608 /* adds statistics information from the packet to the list */
609 static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
610 tap_iax2_stat_t *statinfo, packet_info *pinfo,
611 const struct _iax2_info_t *iax2info _U_)
618 GdkColor color = COLOR_DEFAULT;
620 then = pinfo->fd->abs_ts.secs;
621 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
622 tm_tmp = localtime(&then);
623 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
626 tm_tmp->tm_year + 1900,
632 /* Default to using black on white text if nothing below overrides it */
633 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
635 if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
636 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
638 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
640 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
641 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
642 color = COLOR_WARNING;
643 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
645 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
646 g_snprintf(status,sizeof(status),"Incorrect timestamp");
647 color = COLOR_WARNING;
648 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
650 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
651 && !(statinfo->flags & STAT_FLAG_FIRST)
652 && !(statinfo->flags & STAT_FLAG_PT_CN)
653 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
654 && !(statinfo->flags & STAT_FLAG_MARKER)) {
655 g_snprintf(status,sizeof(status),"Marker missing?");
656 color = COLOR_WARNING;
657 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
660 if (statinfo->flags & STAT_FLAG_MARKER) {
661 color = COLOR_WARNING;
662 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
664 g_snprintf(status,sizeof(status),OK_TEXT);
666 /* is this the first packet we got in this direction? */
667 if (statinfo->flags & STAT_FLAG_FIRST) {
668 add_to_list(list, user_data,
674 timeStr, pinfo->fd->pkt_len,
679 add_to_list(list, user_data,
681 statinfo->delta*1000,
682 statinfo->jitter*1000,
685 timeStr, pinfo->fd->pkt_len,
692 #define MAX_SILENCE_TICKS 1000000
693 /****************************************************************************/
694 static int iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
695 tap_iax2_stat_t *statinfo,
697 const struct _iax2_info_t *iax2info)
702 /* is this the first packet we got in this direction? */
703 if (statinfo->flags & STAT_FLAG_FIRST) {
704 if (saveinfo->fp == NULL) {
705 saveinfo->saved = FALSE;
706 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
709 saveinfo->saved = TRUE;
712 /* save the voice information */
713 /* if there was already an error, we quit */
714 if (saveinfo->saved == FALSE)
717 /* if the captured length and packet length aren't equal, we quit */
718 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
719 saveinfo->saved = FALSE;
720 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
724 if (iax2info->payload_len > 0) {
725 data = iax2info->payload_data;
726 nchars=fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
727 saveinfo->count+=iax2info->payload_len;
729 fflush(saveinfo->fp);
730 saveinfo->saved = TRUE;
738 /****************************************************************************/
741 /****************************************************************************/
743 /****************************************************************************/
744 /* close the dialog window and remove the tap listener */
745 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
747 /* remove tap listener */
748 protect_thread_critical_region();
749 remove_tap_listener(user_data);
750 unprotect_thread_critical_region();
752 /* close and remove temporary files */
753 if (user_data->forward.saveinfo.fp != NULL)
754 fclose(user_data->forward.saveinfo.fp);
755 if (user_data->reversed.saveinfo.fp != NULL)
756 fclose(user_data->reversed.saveinfo.fp);
757 /*XXX: test for error **/
758 ws_remove(user_data->f_tempname);
759 ws_remove(user_data->r_tempname);
761 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
762 /* destroy save_csv_as window if open */
763 if (user_data->dlg.save_csv_as_w != NULL)
764 window_destroy(user_data->dlg.save_csv_as_w);
766 /* destroy save_voice_as window if open */
767 if (user_data->dlg.save_voice_as_w != NULL)
768 window_destroy(user_data->dlg.save_voice_as_w);
770 /* destroy graph window if open */
771 if (user_data->dlg.dialog_graph.window != NULL)
772 window_destroy(user_data->dlg.dialog_graph.window);
774 #ifdef USE_CONVERSATION_GRAPH
775 /* destroy graph window if open */
776 if (user_data->dlg.graph_window != NULL)
777 window_destroy(user_data->dlg.graph_window);
780 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
781 /* XXX: Is this still true for GTK2 ??? */
782 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
788 /****************************************************************************/
789 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
790 GtkNotebookPage *page _U_,
792 user_data_t *user_data _U_)
794 user_data->dlg.selected_list =
795 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
797 user_data->dlg.selected_list_row = 0;
800 /****************************************************************************/
801 static void on_list_select_row(GtkTreeSelection *selection,
802 user_data_t *user_data _U_/*gpointer data */)
804 user_data->dlg.selected_list_sel = selection;
808 #ifdef USE_CONVERSATION_GRAPH
809 Note this will not work any more as clist is removed.
810 /****************************************************************************/
811 /* when the graph window gets destroyed */
812 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
814 /* note that graph window has been destroyed */
815 user_data->dlg.graph_window = NULL;
818 /****************************************************************************/
819 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
822 GtkCList *clist = NULL;
823 if (vp.fnumber != 0) {
824 clist = GTK_CLIST(user_data->dlg.clist_fwd);
825 row = gtk_clist_find_row_from_data(clist,
826 GUINT_TO_POINTER(vp.fnumber));
828 clist = GTK_CLIST(user_data->dlg.clist_rev);
829 row = gtk_clist_find_row_from_data(clist,
830 GUINT_TO_POINTER(vp.fnumber));
833 gtk_notebook_set_current_page(GTK_NOTEBOOK(user_data->dlg.notebook),
834 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
835 gtk_clist_select_row(clist, row, 0);
836 gtk_clist_moveto(clist, row, 0, 0.5, 0);
842 /****************************************************************************/
843 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
849 if (user_data->dlg.graph_window != NULL) {
850 /* There's already a graph window; reactivate it. */
851 reactivate_window(user_data->dlg.graph_window);
854 list = g_list_append(list, &(user_data->series_fwd));
855 list = g_list_append(list, &(user_data->series_rev));
857 user_data->series_fwd.color.pixel = 0;
858 user_data->series_fwd.color.red = 0x80ff;
859 user_data->series_fwd.color.green = 0xe0ff;
860 user_data->series_fwd.color.blue = 0xffff;
861 user_data->series_fwd.yvalue = 0.5;
863 user_data->series_rev.color.pixel = 0;
864 user_data->series_rev.color.red = 0x60ff;
865 user_data->series_rev.color.green = 0xc0ff;
866 user_data->series_rev.color.blue = 0xffff;
867 user_data->series_rev.yvalue = -0.5;
869 g_snprintf(title1, sizeof(title1), "Forward: %s:%u to %s:%u",
870 get_addr_name(&(user_data->ip_src_fwd)),
871 user_data->port_src_fwd,
872 get_addr_name(&(user_data->ip_dst_fwd)),
873 user_data->port_dst_fwd);
875 g_snprintf(title2, sizeof(title2), "Reverse: %s:%u to %s:%u",
876 get_addr_name(&(user_data->ip_src_rev)),
877 user_data->port_src_rev,
878 get_addr_name(&(user_data->ip_dst_rev)),
879 user_data->port_dst_rev);
881 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
882 &graph_selection_callback, user_data);
883 g_signal_connect(user_data->dlg.graph_window, "destroy",
884 G_CALLBACK(on_destroy_graph), user_data);
886 #endif /*USE_CONVERSATION_GRAPH*/
888 /****************************************************************************/
889 static void dialog_graph_set_title(user_data_t* user_data)
892 if (!user_data->dlg.dialog_graph.window){
895 title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
896 get_addr_name(&(user_data->ip_src_fwd)),
897 user_data->port_src_fwd,
898 get_addr_name(&(user_data->ip_dst_fwd)),
899 user_data->port_dst_fwd,
900 get_addr_name(&(user_data->ip_src_rev)),
901 user_data->port_src_rev,
902 get_addr_name(&(user_data->ip_dst_rev)),
903 user_data->port_dst_rev);
905 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
911 /****************************************************************************/
912 static void dialog_graph_reset(user_data_t* user_data)
916 user_data->dlg.dialog_graph.needs_redraw=TRUE;
917 for(i=0;i<MAX_GRAPHS;i++){
918 for(j=0;j<NUM_GRAPH_ITEMS;j++){
919 dialog_graph_graph_item_t *dggi;
920 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
925 user_data->dlg.dialog_graph.last_interval=0xffffffff;
926 user_data->dlg.dialog_graph.max_interval=0;
927 user_data->dlg.dialog_graph.num_items=0;
929 /* create the color titles near the filter buttons */
930 for(i=0;i<MAX_GRAPHS;i++){
933 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
934 sizeof (user_data->dlg.dialog_graph.graph[0].title),
935 "%s: %s:%u to %s:%u",
937 get_addr_name(&(user_data->ip_src_fwd)),
938 user_data->port_src_fwd,
939 get_addr_name(&(user_data->ip_dst_fwd)),
940 user_data->port_dst_fwd);
943 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
944 sizeof(user_data->dlg.dialog_graph.graph[0].title),
945 "%s: %s:%u to %s:%u",
947 get_addr_name(&(user_data->ip_src_rev)),
948 user_data->port_src_rev,
949 get_addr_name(&(user_data->ip_dst_rev)),
950 user_data->port_dst_rev);
954 dialog_graph_set_title(user_data);
957 /****************************************************************************/
958 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
960 dialog_graph_graph_item_t *it;
967 /****************************************************************************/
968 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
971 g_snprintf(buf, buf_len, "%ds",t/1000000);
972 } else if(t>=1000000){
973 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
975 g_snprintf(buf, buf_len, "%dms",t/1000);
977 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
979 g_snprintf(buf, buf_len, "%dus",t);
983 /****************************************************************************/
984 static void dialog_graph_draw(user_data_t* user_data)
987 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
988 gint32 current_interval;
989 guint32 left_x_border;
990 guint32 right_x_border;
991 guint32 top_y_border;
992 guint32 bottom_y_border;
994 int label_width, label_height;
995 guint32 draw_width, draw_height;
996 char label_string[15];
999 guint32 num_time_intervals;
1000 guint32 max_value; /* max value of seen data */
1001 guint32 max_y; /* max value of the Y scale */
1003 if(!user_data->dlg.dialog_graph.needs_redraw){
1006 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1009 * Find the length of the intervals we have data for
1010 * so we know how large arrays we need to malloc()
1012 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1013 /* if there isnt anything to do, just return */
1014 if(num_time_intervals==0){
1017 num_time_intervals+=1;
1018 /* XXX move this check to _packet() */
1019 if(num_time_intervals>NUM_GRAPH_ITEMS){
1020 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
1025 * find the max value so we can autoscale the y axis
1028 for(i=0;i<MAX_GRAPHS;i++){
1031 if(!user_data->dlg.dialog_graph.graph[i].display){
1034 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1037 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1039 /* keep track of the max value we have encountered */
1047 * Clear out old plot
1049 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1050 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1053 user_data->dlg.dialog_graph.draw_area->allocation.width,
1054 user_data->dlg.dialog_graph.draw_area->allocation.height);
1058 * Calculate the y scale we should use
1060 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1061 max_y=yscale_max[MAX_YSCALE-1];
1062 for(i=MAX_YSCALE-1;i>0;i--){
1063 if(max_value<yscale_max[i]){
1064 max_y=yscale_max[i];
1068 /* the user had specified an explicit y scale to use */
1069 max_y=user_data->dlg.dialog_graph.max_y_units;
1073 * Calculate size of borders surrounding the plot
1074 * The border on the right side needs to be adjusted depending
1075 * on the width of the text labels. For simplicity we assume that the
1076 * top y scale label will be the widest one
1078 print_time_scale_string(label_string, sizeof(label_string), max_y);
1079 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1080 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1082 right_x_border=label_width+20;
1084 bottom_y_border=label_height+20;
1088 * Calculate the size of the drawing area for the actual plot
1090 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1091 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1095 * Draw the y axis and labels
1096 * (we always draw the y scale with 11 ticks along the axis)
1098 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1099 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1101 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1102 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1108 /* first, middle and last tick are slightly longer */
1112 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1113 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1114 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1115 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1116 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1117 /* draw the labels */
1119 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1120 pango_layout_set_text(layout, label_string, -1);
1121 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1122 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1123 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1124 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1125 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1129 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1130 pango_layout_set_text(layout, label_string, -1);
1131 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1132 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1133 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1134 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1135 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1139 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1140 pango_layout_set_text(layout, label_string, -1);
1141 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1142 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1143 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1144 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1145 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1153 * if we have not specified the last_interval via the gui,
1154 * then just pick the current end of the capture so that is scrolls
1155 * nicely when doing live captures
1157 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1158 last_interval=user_data->dlg.dialog_graph.max_interval;
1160 last_interval=user_data->dlg.dialog_graph.last_interval;
1167 /* plot the x-scale */
1168 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);
1170 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1171 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1172 first_interval*=user_data->dlg.dialog_graph.interval;
1179 while(interval_delta<((last_interval-first_interval)/10)){
1180 interval_delta*=delta_multiplier;
1181 if(delta_multiplier==5){
1188 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1191 /* if pixels_per_tick is <5, only draw every 10 ticks */
1192 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1196 if(current_interval%interval_delta){
1202 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1203 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1204 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1205 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1206 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1207 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1211 if(user_data->dlg.dialog_graph.interval>=1000){
1212 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1213 } else if(user_data->dlg.dialog_graph.interval>=100){
1214 g_snprintf(label_string, sizeof(label_string),
1215 "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1216 } else if(user_data->dlg.dialog_graph.interval>=10){
1217 g_snprintf(label_string, sizeof(label_string),
1218 "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1220 g_snprintf(label_string, sizeof(label_string),
1221 "%d.%3ds", current_interval/1000,current_interval%1000);
1223 pango_layout_set_text(layout, label_string, -1);
1224 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1225 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1226 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1227 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1228 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1240 * Draw "x" for Sequence Errors and "m" for Marks
1242 /* Draw the labels Fwd and Rev */
1243 g_strlcpy(label_string, "<-Fwd", sizeof(label_string));
1244 pango_layout_set_text(layout, label_string, -1);
1245 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1246 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1247 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1248 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1249 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1251 g_strlcpy(label_string, "<-Rev", sizeof(label_string));
1252 pango_layout_set_text(layout, label_string, -1);
1253 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1254 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1255 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1256 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1257 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1260 /* Draw the marks */
1261 for(i=MAX_GRAPHS-1;i>=0;i--){
1263 guint32 x_pos, prev_x_pos;
1265 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1266 if (!user_data->dlg.dialog_graph.graph[i].display){
1269 /* initialize prev x/y to the low left corner of the graph */
1270 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;
1272 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1273 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;
1275 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1277 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1278 g_strlcpy(label_string, "x", sizeof(label_string));
1280 g_strlcpy(label_string, "m", sizeof(label_string));
1283 pango_layout_set_text(layout, label_string, -1);
1284 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1285 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1286 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1288 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1296 g_object_unref(G_OBJECT(layout));
1299 * Loop over all graphs and draw them
1301 for(i=MAX_GRAPHS-1;i>=0;i--){
1303 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1304 if (!user_data->dlg.dialog_graph.graph[i].display){
1307 /* initialize prev x/y to the low left corner of the graph */
1308 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;
1309 prev_y_pos=draw_height-1+top_y_border;
1311 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1313 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;
1314 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1318 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1321 /* dont need to draw anything if the segment
1322 * is entirely above the top of the graph
1324 if( (prev_y_pos==0) && (y_pos==0) ){
1331 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1332 x_pos, draw_height-1+top_y_border,
1342 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1343 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1344 user_data->dlg.dialog_graph.pixmap,
1347 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1350 /* update the scrollbar */
1351 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1352 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1353 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1354 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1355 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1357 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1359 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1360 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1361 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1365 /****************************************************************************/
1366 static void dialog_graph_redraw(user_data_t* user_data)
1368 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1369 dialog_graph_draw(user_data);
1372 /****************************************************************************/
1374 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1376 user_data->dlg.dialog_graph.window = NULL;
1379 /****************************************************************************/
1380 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1382 user_data_t *user_data;
1384 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1386 exit(10); /* !! XXX: should be g_assert or something ? */
1390 gdk_draw_pixmap(widget->window,
1391 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1392 user_data->dlg.dialog_graph.pixmap,
1393 event->area.x, event->area.y,
1394 event->area.x, event->area.y,
1395 event->area.width, event->area.height);
1400 /****************************************************************************/
1401 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1403 user_data_t *user_data;
1406 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1412 if(user_data->dlg.dialog_graph.pixmap){
1413 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1414 user_data->dlg.dialog_graph.pixmap=NULL;
1417 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1418 widget->allocation.width,
1419 widget->allocation.height,
1421 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1422 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1424 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1425 widget->style->white_gc,
1428 widget->allocation.width,
1429 widget->allocation.height);
1431 /* set up the colors and the GC structs for this pixmap */
1432 for(i=0;i<MAX_GRAPHS;i++){
1433 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1434 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1437 dialog_graph_redraw(user_data);
1441 /****************************************************************************/
1442 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1444 user_data_t *user_data=(user_data_t *)data;
1447 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1448 if(user_data->dlg.dialog_graph.last_interval==mi){
1451 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1452 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1456 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1458 dialog_graph_redraw(user_data);
1462 /****************************************************************************/
1463 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1465 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1466 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1467 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1469 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);
1471 /* signals needed to handle backing pixmap */
1472 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1473 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1475 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1476 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1478 /* create the associated scrollbar */
1479 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1480 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1481 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1482 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1483 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1486 /****************************************************************************/
1487 static void disable_graph(dialog_graph_graph_t *dgg)
1491 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1496 /****************************************************************************/
1497 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1499 /* this graph is not active, just update display and redraw */
1500 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1502 dialog_graph_redraw(dgg->ud);
1507 cf_retap_packets(&cfile, FALSE);
1508 dialog_graph_redraw(dgg->ud);
1513 /****************************************************************************/
1514 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1520 hbox=gtk_hbox_new(FALSE, 3);
1521 gtk_container_add(GTK_CONTAINER(box), hbox);
1522 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1523 gtk_widget_show(hbox);
1525 g_snprintf(str, sizeof(str), "Graph %d", num);
1526 dgg->display_button=gtk_toggle_button_new_with_label(str);
1527 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1528 gtk_widget_show(dgg->display_button);
1529 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1530 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1532 label=gtk_label_new(dgg->title);
1533 gtk_widget_show(label);
1534 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1536 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1537 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1538 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1539 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1540 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1545 /****************************************************************************/
1546 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1553 frame=gtk_frame_new("Graphs");
1554 gtk_container_add(GTK_CONTAINER(box), frame);
1555 gtk_widget_show(frame);
1557 vbox=gtk_vbox_new(FALSE, 1);
1558 gtk_container_add(GTK_CONTAINER(frame), vbox);
1559 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1560 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1561 gtk_widget_show(vbox);
1563 for(i=0;i<MAX_GRAPHS;i++){
1564 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1567 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1568 gtk_widget_show(label);
1569 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1574 /****************************************************************************/
1575 static void yscale_select(GtkWidget *item, gpointer key)
1578 user_data_t *user_data;
1580 user_data=(user_data_t *)key;
1581 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1583 user_data->dlg.dialog_graph.max_y_units=val;
1584 dialog_graph_redraw(user_data);
1587 /****************************************************************************/
1588 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1591 user_data_t *user_data;
1593 user_data=(user_data_t *)key;
1594 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1595 user_data->dlg.dialog_graph.pixels_per_tick=val;
1596 dialog_graph_redraw(user_data);
1599 /****************************************************************************/
1600 static void tick_interval_select(GtkWidget *item, gpointer key)
1603 user_data_t *user_data;
1605 user_data=(user_data_t *)key;
1606 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1608 user_data->dlg.dialog_graph.interval=val;
1609 cf_retap_packets(&cfile, FALSE);
1610 dialog_graph_redraw(user_data);
1613 /****************************************************************************/
1614 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1617 GtkWidget *menu_item;
1620 for(i=0;i<MAX_YSCALE;i++){
1621 if(yscale_max[i]==AUTO_MAX_YSCALE){
1622 g_strlcpy(str,"Auto",15);
1624 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1626 menu_item=gtk_menu_item_new_with_label(str);
1627 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1628 GUINT_TO_POINTER(yscale_max[i]));
1629 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1630 gtk_widget_show(menu_item);
1631 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1636 /****************************************************************************/
1637 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1640 GtkWidget *menu_item;
1643 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1644 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1645 menu_item=gtk_menu_item_new_with_label(str);
1647 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1648 GUINT_TO_POINTER(pixels_per_tick[i]));
1649 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1650 gtk_widget_show(menu_item);
1651 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1653 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1658 /****************************************************************************/
1659 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1662 GtkWidget *menu_item;
1665 for(i=0;i<MAX_TICK_VALUES;i++){
1666 if(tick_interval_values[i]>=1000){
1667 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1668 } else if(tick_interval_values[i]>=100){
1669 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1670 } else if(tick_interval_values[i]>=10){
1671 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1673 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1676 menu_item=gtk_menu_item_new_with_label(str);
1677 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1678 GUINT_TO_POINTER(tick_interval_values[i]));
1679 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1680 gtk_widget_show(menu_item);
1681 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1683 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1687 /****************************************************************************/
1688 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1692 GtkWidget *option_menu;
1695 hbox=gtk_hbox_new(FALSE, 0);
1696 gtk_container_add(GTK_CONTAINER(box), hbox);
1697 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1698 gtk_widget_show(hbox);
1700 label=gtk_label_new(name);
1701 gtk_widget_show(label);
1702 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1704 option_menu=gtk_option_menu_new();
1705 menu=gtk_menu_new();
1706 (*func)(user_data, menu);
1707 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1708 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1709 gtk_widget_show(option_menu);
1712 /****************************************************************************/
1713 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1715 GtkWidget *frame_vbox;
1719 frame_vbox=gtk_vbox_new(FALSE, 0);
1720 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1721 gtk_widget_show(frame_vbox);
1723 frame = gtk_frame_new("X Axis");
1724 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1725 gtk_widget_show(frame);
1727 vbox=gtk_vbox_new(FALSE, 0);
1728 gtk_container_add(GTK_CONTAINER(frame), vbox);
1729 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1730 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1731 gtk_widget_show(vbox);
1733 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1734 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1736 frame = gtk_frame_new("Y Axis");
1737 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1738 gtk_widget_show(frame);
1740 vbox=gtk_vbox_new(FALSE, 0);
1741 gtk_container_add(GTK_CONTAINER(frame), vbox);
1742 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1743 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1744 gtk_widget_show(vbox);
1746 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1751 /****************************************************************************/
1752 static void dialog_graph_init_window(user_data_t* user_data)
1756 GtkWidget *bt_close;
1758 /* create the main window */
1759 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1761 vbox=gtk_vbox_new(FALSE, 0);
1762 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1763 gtk_widget_show(vbox);
1765 create_draw_area(user_data, vbox);
1767 hbox=gtk_hbox_new(FALSE, 3);
1768 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1769 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1770 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1771 gtk_widget_show(hbox);
1773 create_filter_area(user_data, hbox);
1774 create_ctrl_area(user_data, hbox);
1776 dialog_graph_set_title(user_data);
1778 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1779 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1780 gtk_widget_show(hbox);
1782 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1783 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1785 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1787 gtk_widget_show(user_data->dlg.dialog_graph.window);
1788 window_present(user_data->dlg.dialog_graph.window);
1793 /****************************************************************************/
1794 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1796 if (user_data->dlg.dialog_graph.window != NULL) {
1797 /* There's already a graph window; reactivate it. */
1798 reactivate_window(user_data->dlg.dialog_graph.window);
1802 dialog_graph_init_window(user_data);
1806 /****************************************************************************/
1807 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1810 GtkTreeModel *model;
1811 GtkTreeSelection *selection;
1814 selection = user_data->dlg.selected_list_sel;
1816 if (selection==NULL)
1819 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1820 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1821 cf_goto_frame(&cfile, fnumber);
1826 static void draw_stat(user_data_t *user_data);
1828 /****************************************************************************/
1829 /* re-dissects all packets */
1830 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1832 GString *error_string;
1834 /* remove tap listener */
1835 protect_thread_critical_region();
1836 remove_tap_listener(user_data);
1837 unprotect_thread_critical_region();
1839 /* register tap listener */
1840 error_string = register_tap_listener("IAX2", user_data, NULL,
1841 iax2_reset, iax2_packet, iax2_draw);
1842 if (error_string != NULL) {
1843 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1844 g_string_free(error_string, TRUE);
1848 /* retap all packets */
1849 cf_retap_packets(&cfile, FALSE);
1851 /* draw statistics info */
1852 draw_stat(user_data);
1856 /****************************************************************************/
1857 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1860 GtkTreeModel *model;
1862 GtkTreeSelection *selection;
1865 selection = user_data->dlg.selected_list_sel;
1867 if (selection==NULL)
1871 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1872 while (gtk_tree_model_iter_next (model,&iter)) {
1873 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1874 if (strcmp(text, OK_TEXT) != 0) {
1875 gtk_tree_selection_select_iter (selection, &iter);
1876 path = gtk_tree_model_get_path(model, &iter);
1877 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1880 gtk_tree_path_free(path);
1887 if (user_data->dlg.number_of_nok>1){
1888 /* Get the first iter and select it before starting over */
1889 gtk_tree_model_get_iter_first(model, &iter);
1890 gtk_tree_selection_select_iter (selection, &iter);
1896 /****************************************************************************/
1897 /* when we want to save the information */
1898 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1901 GtkWidget *rev, *forw, *both;
1902 user_data_t *user_data;
1904 GtkListStore *store;
1906 GtkTreeModel *model;
1907 gboolean more_items = TRUE;
1909 /* To Hold data from the list row */
1910 guint packet; /* Packet */
1911 gfloat delta; /* Delta(ms) */
1912 gfloat jitter; /* Jitter(ms) */
1913 gfloat ipbw; /* IP BW(kbps) */
1914 char *status_str; /* Status */
1915 char *date_str; /* Date */
1916 guint length; /* Length */
1921 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1923 /* Perhaps the user specified a directory instead of a file.
1924 * Check whether they did.
1926 if (test_for_directory(g_dest) == EISDIR) {
1927 /* It's a directory - set the file selection box to display it. */
1928 set_last_open_dir(g_dest);
1929 file_selection_set_current_folder(fc, get_last_open_dir());
1930 gtk_file_chooser_set_current_name(fc, "");
1932 return FALSE; /* run the dialog again */
1934 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1935 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1936 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1937 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1939 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1940 fp = ws_fopen(g_dest, "w");
1942 open_failure_alert_box(g_dest, errno, TRUE);
1944 return TRUE; /* we're done */
1947 if (GTK_TOGGLE_BUTTON(both)->active) {
1948 fprintf(fp, "Forward\n");
1950 write_failure_alert_box(g_dest, errno);
1953 return TRUE; /* we're done */
1957 for(j = 0; j < NUM_COLS; j++) {
1959 fprintf(fp,"%s",titles[j]);
1961 fprintf(fp,",%s",titles[j]);
1966 write_failure_alert_box(g_dest, errno);
1969 return TRUE; /* we're done */
1971 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1972 store = GTK_LIST_STORE(model);
1973 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1975 while (more_items) {
1976 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1985 fprintf(fp, "%u",packet);
1986 fprintf(fp, ",%.2f", delta);
1987 fprintf(fp, ",%.2f", jitter);
1988 fprintf(fp, ",%.2f", ipbw);
1989 fprintf(fp, ",%s", status_str);
1990 fprintf(fp, ",%s", date_str);
1991 fprintf(fp, ",%u", length);
1996 write_failure_alert_box(g_dest, errno);
1999 return TRUE; /* we're done */
2002 more_items = gtk_tree_model_iter_next (model,&iter);
2006 if (fclose(fp) == EOF) {
2007 write_failure_alert_box(g_dest, errno);
2009 return TRUE; /* we're done */
2013 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2015 if (GTK_TOGGLE_BUTTON(both)->active) {
2016 fp = ws_fopen(g_dest, "a");
2018 open_failure_alert_box(g_dest, errno, TRUE);
2020 return TRUE; /* we're done */
2022 fprintf(fp, "\nReverse\n");
2024 write_failure_alert_box(g_dest, errno);
2027 return TRUE; /* we're done */
2030 fp = ws_fopen(g_dest, "w");
2032 open_failure_alert_box(g_dest, errno, TRUE);
2034 return TRUE; /* we're done */
2037 for(j = 0; j < NUM_COLS; j++) {
2039 fprintf(fp,"%s",titles[j]);
2041 fprintf(fp,",%s",titles[j]);
2046 write_failure_alert_box(g_dest, errno);
2049 return TRUE; /* we're done */
2051 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2052 store = GTK_LIST_STORE(model);
2053 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2057 while (more_items) {
2058 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2067 fprintf(fp, "%u",packet);
2068 fprintf(fp, ",%.2f", delta);
2069 fprintf(fp, ",%.2f", jitter);
2070 fprintf(fp, ",%.2f", ipbw);
2071 fprintf(fp, ",%s", status_str);
2072 fprintf(fp, ",%s", date_str);
2073 fprintf(fp, ",%u", length);
2078 write_failure_alert_box(g_dest, errno);
2081 return TRUE; /* we're done */
2084 more_items = gtk_tree_model_iter_next (model,&iter);
2087 if (fclose(fp) == EOF) {
2088 write_failure_alert_box(g_dest, errno);
2090 return TRUE; /* we're done */
2094 return TRUE; /* we're done */
2097 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2099 user_data->dlg.save_csv_as_w = NULL;
2102 /* when the user wants to save the csv information in a file */
2103 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2107 GtkWidget *label_format;
2108 GtkWidget *channels_label;
2109 GSList *channels_group = NULL;
2110 GtkWidget *forward_rb;
2111 GtkWidget *reversed_rb;
2114 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2115 if (user_data->dlg.save_csv_as_w != NULL) {
2116 /* There's already a Save CSV info dialog box; reactivate it. */
2117 reactivate_window(user_data->dlg.save_csv_as_w);
2121 user_data->dlg.save_csv_as_w =
2122 gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2123 GTK_WINDOW(user_data->dlg.notebook),
2124 GTK_FILE_CHOOSER_ACTION_SAVE,
2125 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2126 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2128 #if GTK_CHECK_VERSION(2,8,0)
2129 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2131 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2133 /* Build our "extra widget" to be added to the file chooser widget */
2134 /* Container for each row of widgets */
2135 vertb = gtk_vbox_new(FALSE, 0);
2136 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2137 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2138 gtk_widget_show (vertb);
2140 table1 = gtk_table_new (2, 4, FALSE);
2141 gtk_widget_show (table1);
2142 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2143 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2144 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2146 label_format = gtk_label_new ("Format: Comma Separated Values");
2147 gtk_widget_show (label_format);
2148 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2149 (GtkAttachOptions) (GTK_FILL),
2150 (GtkAttachOptions) (0), 0, 0);
2151 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2153 channels_label = gtk_label_new ("Channels: ");
2154 gtk_widget_show (channels_label);
2155 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2156 (GtkAttachOptions) (GTK_FILL),
2157 (GtkAttachOptions) (0), 0, 0);
2158 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2160 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2161 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2162 gtk_widget_show (forward_rb);
2163 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2164 (GtkAttachOptions) (GTK_FILL),
2165 (GtkAttachOptions) (0), 0, 0);
2167 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2168 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2169 gtk_widget_show (reversed_rb);
2170 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2171 (GtkAttachOptions) (GTK_FILL),
2172 (GtkAttachOptions) (0), 0, 0);
2174 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2175 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2176 gtk_widget_show (both_rb);
2177 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2178 (GtkAttachOptions) (GTK_FILL),
2179 (GtkAttachOptions) (0), 0, 0);
2181 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2183 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2184 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2185 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2186 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2188 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2189 G_CALLBACK(window_delete_event_cb), NULL);
2190 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2191 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2193 gtk_widget_show(user_data->dlg.save_csv_as_w);
2194 window_present(user_data->dlg.save_csv_as_w);
2196 /* "Run" the GtkFileChooserDialog. */
2197 /* Upon exit: If "Accept" run the OK callback. */
2198 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2199 /* Destroy the window. */
2200 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2201 /* return with a TRUE status so that the dialog window will be destroyed. */
2202 /* Trying to re-run the dialog after popping up an alert box will not work */
2203 /* since the user will not be able to dismiss the alert box. */
2204 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2205 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2207 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2208 /* GtkFileChooserDialog. */
2209 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2210 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2211 break; /* we're done */
2214 window_destroy(user_data->dlg.save_csv_as_w);
2217 /****************************************************************************/
2218 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2220 /* Note that we no longer have a Save voice info dialog box. */
2221 user_data->dlg.save_voice_as_w = NULL;
2224 /****************************************************************************/
2225 /* here we save it into a file that user specified */
2226 /* XXX what about endians here? could go something wrong? */
2227 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2229 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2230 gchar f_pd[1] = {0};
2231 gchar r_pd[1] = {0};
2234 guint32 f_write_silence = 0;
2235 guint32 r_write_silence = 0;
2237 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2238 gboolean stop_flag = FALSE;
2241 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2244 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2250 /* open file for saving */
2251 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2258 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2260 if (format == SAVE_AU_FORMAT) /* au format */
2262 /* First we write the .au header. XXX Hope this is endian independant */
2263 /* the magic word 0x2e736e64 == .snd */
2264 phtonl(pd, 0x2e736e64);
2265 nchars=ws_write(to_fd, pd, 4);
2266 /* header offset == 24 bytes */
2268 nchars=ws_write(to_fd, pd, 4);
2269 /* total length, it is permited to set this to 0xffffffff */
2271 nchars=ws_write(to_fd, pd, 4);
2272 /* encoding format == 16-bit linear PCM */
2274 nchars=ws_write(to_fd, pd, 4);
2275 /* sample rate == 8000 Hz */
2277 nchars=ws_write(to_fd, pd, 4);
2280 nchars=ws_write(to_fd, pd, 4);
2284 /* only forward direction */
2285 case SAVE_FORWARD_DIRECTION_MASK: {
2286 progbar_count = user_data->forward.saveinfo.count;
2287 progbar_quantum = user_data->forward.saveinfo.count/100;
2288 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2291 if((count > progbar_nextstep) && (count <= progbar_count)) {
2292 update_progress_dlg(progbar,
2293 (gfloat) count/progbar_count, "Saving");
2294 progbar_nextstep = progbar_nextstep + progbar_quantum;
2298 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2299 sample = ulaw2linear(*f_pd);
2302 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2303 sample = alaw2linear(*f_pd);
2310 destroy_progress_dlg(progbar);
2314 fwritten = ws_write(to_fd, pd, 2);
2315 if ((fwritten < 2) || (fwritten < 0) || (fread < 0)) {
2319 destroy_progress_dlg(progbar);
2325 /* only reversed direction */
2326 case SAVE_REVERSE_DIRECTION_MASK: {
2327 progbar_count = user_data->reversed.saveinfo.count;
2328 progbar_quantum = user_data->reversed.saveinfo.count/100;
2329 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2332 if((count > progbar_nextstep) && (count <= progbar_count)) {
2333 update_progress_dlg(progbar,
2334 (gfloat) count/progbar_count, "Saving");
2335 progbar_nextstep = progbar_nextstep + progbar_quantum;
2339 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2340 sample = ulaw2linear(*r_pd);
2343 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2344 sample = alaw2linear(*r_pd);
2351 destroy_progress_dlg(progbar);
2355 rwritten = ws_write(to_fd, pd, 2);
2356 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2360 destroy_progress_dlg(progbar);
2366 /* both directions */
2367 case SAVE_BOTH_DIRECTION_MASK: {
2368 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2369 (progbar_count = user_data->forward.saveinfo.count) :
2370 (progbar_count = user_data->reversed.saveinfo.count);
2371 progbar_quantum = progbar_count/100;
2372 /* since conversation in one way can start later than in the other one,
2373 * we have to write some silence information for one channel */
2374 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2375 f_write_silence = (guint32)
2376 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2378 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2379 r_write_silence = (guint32)
2380 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2385 if((count > progbar_nextstep) && (count <= progbar_count)) {
2386 update_progress_dlg(progbar,
2387 (gfloat) count/progbar_count, "Saving");
2388 progbar_nextstep = progbar_nextstep + progbar_quantum;
2391 if(f_write_silence > 0) {
2392 rread = read(rev_fd, r_pd, 1);
2393 switch (user_data->forward.statinfo.reg_pt) {
2394 case AST_FORMAT_ULAW:
2395 *f_pd = SILENCE_PCMU;
2397 case AST_FORMAT_ALAW:
2398 *f_pd = SILENCE_PCMA;
2404 else if(r_write_silence > 0) {
2405 fread = read(forw_fd, f_pd, 1);
2406 switch (user_data->reversed.statinfo.reg_pt) {
2407 case AST_FORMAT_ULAW:
2408 *r_pd = SILENCE_PCMU;
2410 case AST_FORMAT_ALAW:
2411 *r_pd = SILENCE_PCMA;
2418 fread = read(forw_fd, f_pd, 1);
2419 rread = read(rev_fd, r_pd, 1);
2421 if ((rread == 0) && (fread == 0))
2423 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2424 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2427 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2428 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2436 destroy_progress_dlg(progbar);
2441 rwritten = ws_write(to_fd, pd, 2);
2442 if ((rwritten < 2) || (rread < 0) || (fread < 0)) {
2446 destroy_progress_dlg(progbar);
2453 else if (format == SAVE_RAW_FORMAT) /* raw format */
2457 /* only forward direction */
2458 case SAVE_FORWARD_DIRECTION_MASK: {
2459 progbar_count = user_data->forward.saveinfo.count;
2460 progbar_quantum = user_data->forward.saveinfo.count/100;
2464 /* only reversed direction */
2465 case SAVE_REVERSE_DIRECTION_MASK: {
2466 progbar_count = user_data->reversed.saveinfo.count;
2467 progbar_quantum = user_data->reversed.saveinfo.count/100;
2475 destroy_progress_dlg(progbar);
2482 /* XXX how do you just copy the file? */
2483 while ((rread = read(fd, pd, 1)) > 0) {
2486 if((count > progbar_nextstep) && (count <= progbar_count)) {
2487 update_progress_dlg(progbar,
2488 (gfloat) count/progbar_count, "Saving");
2489 progbar_nextstep = progbar_nextstep + progbar_quantum;
2493 rwritten = ws_write(to_fd, pd, 1);
2495 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2499 destroy_progress_dlg(progbar);
2505 destroy_progress_dlg(progbar);
2513 /****************************************************************************/
2514 /* the user wants to save in a file */
2515 /* XXX support for different formats is currently commented out */
2516 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2519 /*GtkWidget *wav, *sw;*/
2520 GtkWidget *au, *raw;
2521 GtkWidget *rev, *forw, *both;
2522 user_data_t *user_data;
2523 gint channels , format;
2525 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2527 /* Perhaps the user specified a directory instead of a file.
2528 Check whether they did. */
2529 if (test_for_directory(g_dest) == EISDIR) {
2530 /* It's a directory - set the file selection box to display it. */
2531 set_last_open_dir(g_dest);
2532 file_selection_set_current_folder(fc, get_last_open_dir());
2533 gtk_file_chooser_set_current_name(fc, "");
2535 return FALSE; /* run the dialog again */
2539 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2540 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2542 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2543 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2544 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2545 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2546 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2547 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2549 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2550 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2551 * disable the ok button or disable the buttons for direction if only one is not ok. The
2552 * problem is if we open the save voice dialog and then click the refresh button and maybe
2553 * the state changes, so we can't save anymore. In this case we should be able to update
2554 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2557 /* we can not save in both directions */
2558 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2559 /* there are many combinations here, we just exit when first matches */
2560 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2561 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2562 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2563 "Can't save in a file: Unsupported codec!");
2564 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2565 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2566 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2567 "Can't save in a file: Wrong length of captured packets!");
2568 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2569 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2570 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2571 "Can't save in a file: Not all data in all packets was captured!");
2573 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2574 "Can't save in a file: File I/O problem!");
2576 return TRUE; /* we're done */
2578 /* we can not save forward direction */
2579 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2580 (GTK_TOGGLE_BUTTON (both)->active))) {
2581 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2582 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2583 "Can't save forward direction in a file: Unsupported codec!");
2584 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2585 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2586 "Can't save forward direction in a file: Wrong length of captured packets!");
2587 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2588 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2589 "Can't save forward direction in a file: Not all data in all packets was captured!");
2591 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2592 "Can't save forward direction in a file: File I/O problem!");
2594 return TRUE; /* we're done */
2596 /* we can not save reversed direction */
2597 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2598 (GTK_TOGGLE_BUTTON (both)->active))) {
2599 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2600 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2601 "Can't save reversed direction in a file: Unsupported codec!");
2602 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2603 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2604 "Can't save reversed direction in a file: Wrong length of captured packets!");
2605 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2606 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2607 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2608 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2609 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2610 "Can't save reversed direction in a file: No IAX2 data!");
2612 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2613 "Can't save reversed direction in a file: File I/O problem!");
2615 return TRUE; /* we're done */
2619 if (GTK_TOGGLE_BUTTON (wav)->active)
2620 format = SAVE_WAV_FORMAT;
2623 if (GTK_TOGGLE_BUTTON (au)->active)
2624 format = SAVE_AU_FORMAT;
2626 else if (GTK_TOGGLE_BUTTON (sw)->active)
2627 format = SAVE_SW_FORMAT;
2629 else if (GTK_TOGGLE_BUTTON (raw)->active)
2630 format = SAVE_RAW_FORMAT;
2632 format = SAVE_NONE_FORMAT;
2634 if (GTK_TOGGLE_BUTTON (rev)->active)
2635 channels = SAVE_REVERSE_DIRECTION_MASK;
2636 else if (GTK_TOGGLE_BUTTON (both)->active)
2637 channels = SAVE_BOTH_DIRECTION_MASK;
2639 channels = SAVE_FORWARD_DIRECTION_MASK;
2641 /* direction/format validity*/
2642 if (format == SAVE_AU_FORMAT)
2644 /* make sure streams are alaw/ulaw */
2645 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2646 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2647 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2649 return TRUE; /* we're done */
2651 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2652 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2653 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2655 return TRUE; /* we're done */
2657 /* make sure pt's don't differ */
2658 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2659 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2660 "Can't save in a file: Forward and reverse direction differ in type");
2662 return TRUE; /* we're done */
2665 else if (format == SAVE_RAW_FORMAT)
2667 /* can't save raw in both directions */
2668 if (channels == SAVE_BOTH_DIRECTION_MASK){
2669 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2670 "Can't save in a file: Unable to save raw data in both directions");
2672 return TRUE; /* we're done */
2677 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2678 "Can't save in a file: Invalid save format");
2680 return TRUE; /* we're done */
2683 if(!copy_file(g_dest, channels, format, user_data)) {
2684 /* XXX - report the error type! */
2685 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2686 "An error occurred while saving voice in a file!");
2688 return TRUE; /* we're done */
2691 return TRUE; /* we're done */
2694 /****************************************************************************/
2695 /* when the user wants to save the voice information in a file */
2696 /* XXX support for different formats is currently commented out */
2697 static void save_voice_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2701 GtkWidget *label_format;
2702 GtkWidget *channels_label;
2703 GSList *format_group = NULL;
2704 GSList *channels_group = NULL;
2705 GtkWidget *forward_rb;
2706 GtkWidget *reversed_rb;
2708 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2712 /* if we can't save in a file: wrong codec, cut packets or other errors */
2713 /* should the error arise here or later when you click ok button ?
2714 * if we do it here, then we must disable the refresh button, so we don't do it here */
2716 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2717 if (user_data->dlg.save_voice_as_w != NULL) {
2718 /* There's already a Save voice info dialog box; reactivate it. */
2719 reactivate_window(user_data->dlg.save_voice_as_w);
2723 /* XXX - use file_selection from dlg_utils instead! */
2724 user_data->dlg.save_voice_as_w =
2725 gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2726 GTK_WINDOW(user_data->dlg.notebook),
2727 GTK_FILE_CHOOSER_ACTION_SAVE,
2728 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2729 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2731 #if GTK_CHECK_VERSION(2,8,0)
2732 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2734 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2736 /* Container for each row of widgets */
2737 vertb = gtk_vbox_new(FALSE, 0);
2738 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2739 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2740 gtk_widget_show (vertb);
2742 table1 = gtk_table_new (2, 4, FALSE);
2743 gtk_widget_show (table1);
2744 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2745 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2746 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2749 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2750 gtk_widget_show (label_format);
2751 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2752 (GtkAttachOptions) (GTK_FILL),
2753 (GtkAttachOptions) (0), 0, 0);
2756 label_format = gtk_label_new ("Format: ");
2757 gtk_widget_show (label_format);
2758 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2759 (GtkAttachOptions) (GTK_FILL),
2760 (GtkAttachOptions) (0), 0, 0);
2762 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2764 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2765 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2766 gtk_widget_show (raw_rb);
2767 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2768 (GtkAttachOptions) (GTK_FILL),
2769 (GtkAttachOptions) (0), 0, 0);
2772 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2773 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2774 gtk_widget_show (au_rb);
2775 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2776 (GtkAttachOptions) (GTK_FILL),
2777 (GtkAttachOptions) (0), 0, 0);
2780 /* we support .au - ulaw*/
2781 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2782 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2783 gtk_widget_show (wav_rb);
2784 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2785 (GtkAttachOptions) (GTK_FILL),
2786 (GtkAttachOptions) (0), 0, 0);
2788 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2789 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2790 gtk_widget_show (sw_rb);
2791 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2792 (GtkAttachOptions) (GTK_FILL),
2793 (GtkAttachOptions) (0), 0, 0);
2794 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2795 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2796 gtk_widget_show (au_rb);
2797 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2798 (GtkAttachOptions) (GTK_FILL),
2799 (GtkAttachOptions) (0), 0, 0);
2802 channels_label = gtk_label_new ("Channels: ");
2803 gtk_widget_show (channels_label);
2804 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2805 (GtkAttachOptions) (GTK_FILL),
2806 (GtkAttachOptions) (0), 0, 0);
2807 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2809 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2810 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2811 gtk_widget_show (forward_rb);
2812 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2813 (GtkAttachOptions) (GTK_FILL),
2814 (GtkAttachOptions) (0), 0, 0);
2816 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2817 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2818 gtk_widget_show (reversed_rb);
2819 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2820 (GtkAttachOptions) (GTK_FILL),
2821 (GtkAttachOptions) (0), 0, 0);
2823 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2824 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2825 gtk_widget_show (both_rb);
2826 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2827 (GtkAttachOptions) (GTK_FILL),
2828 (GtkAttachOptions) (0), 0, 0);
2830 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2833 /* if one direction is nok we don't allow saving
2834 XXX this is not ok since the user can click the refresh button and cause changes
2835 but we can not update this window. So we move all the decision on the time the ok
2836 button is clicked */
2837 if (user_data->forward.saved == FALSE) {
2838 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2839 gtk_widget_set_sensitive(forward_rb, FALSE);
2840 gtk_widget_set_sensitive(both_rb, FALSE);
2842 else if (user_data->reversed.saved == FALSE) {
2843 gtk_widget_set_sensitive(reversed_rb, FALSE);
2844 gtk_widget_set_sensitive(both_rb, FALSE);
2848 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2849 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2850 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2851 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2852 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2853 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2854 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2855 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2857 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2858 G_CALLBACK(window_delete_event_cb), NULL);
2859 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2860 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2862 gtk_widget_show(user_data->dlg.save_voice_as_w);
2863 window_present(user_data->dlg.save_voice_as_w);
2865 /* "Run" the GtkFileChooserDialog. */
2866 /* Upon exit: If "Accept" run the OK callback. */
2867 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2868 /* Destroy the window. */
2869 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2870 /* return with a TRUE status so that the dialog window will be destroyed. */
2871 /* Trying to re-run the dialog after popping up an alert box will not work */
2872 /* since the user will not be able to dismiss the alert box. */
2873 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2874 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2876 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2877 /* GtkFileChooserDialog. */
2878 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2879 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2880 break; /* we're done */
2883 window_destroy(user_data->dlg.save_voice_as_w);
2887 /****************************************************************************/
2888 /* when we are finished with redisection, we add the label for the statistic */
2889 static void draw_stat(user_data_t *user_data)
2891 gchar label_max[200];
2893 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2894 user_data->forward.statinfo.total_nr,
2895 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2897 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2899 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2900 user_data->reversed.statinfo.total_nr,
2901 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2903 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2910 /****************************************************************************/
2911 /* append a line to list */
2912 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2913 double delta, double jitter, double bandwidth, gchar *status,
2914 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2916 GtkListStore *list_store;
2918 if (strcmp(status, OK_TEXT) != 0) {
2919 user_data->dlg.number_of_nok++;
2922 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2924 /* Creates a new row at position. iter will be changed to point to this new row.
2925 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2926 * The row will be filled with the values given to this function.
2928 * should generally be preferred when inserting rows in a sorted list store.
2930 #if GTK_CHECK_VERSION(2,6,0)
2931 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2932 PACKET_COLUMN, number,
2933 DELTA_COLUMN, delta,
2934 JITTER_COLUMN, jitter,
2935 IPBW_COLUMN, bandwidth,
2936 STATUS_COLUMN, (char *)status,
2937 DATE_COLUMN, (char *)timeStr,
2938 LENGTH_COLUMN, pkt_len,
2939 FOREGROUND_COLOR_COL, NULL,
2940 BACKGROUND_COLOR_COL, (char *)color_str,
2944 gtk_list_store_append (list_store, &user_data->dlg.iter);
2945 gtk_list_store_set (list_store, &user_data->dlg.iter,
2946 PACKET_COLUMN, number,
2947 DELTA_COLUMN, delta,
2948 JITTER_COLUMN, jitter,
2949 IPBW_COLUMN, bandwidth,
2950 STATUS_COLUMN, (char *)status,
2951 DATE_COLUMN, (char *)timeStr,
2952 LENGTH_COLUMN, pkt_len,
2953 FOREGROUND_COLOR_COL, NULL,
2954 BACKGROUND_COLOR_COL, (char *)color_str,
2957 if(flags & STAT_FLAG_FIRST){
2958 /* Set first row as active */
2959 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2963 /****************************************************************************
2964 * Functions needed to present values from the list
2967 /* Present floats with two decimals */
2969 iax2_float_data_func (GtkTreeViewColumn *column _U_,
2970 GtkCellRenderer *renderer,
2971 GtkTreeModel *model,
2979 /* the col to get data from is in userdata */
2980 gint float_col = GPOINTER_TO_INT(user_data);
2982 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
2984 /* save the current locale */
2985 savelocale = setlocale(LC_NUMERIC, NULL);
2986 /* switch to "C" locale to avoid problems with localized decimal separators
2987 * in g_snprintf("%f") functions
2989 setlocale(LC_NUMERIC, "C");
2991 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
2992 /* restore previous locale setting */
2993 setlocale(LC_NUMERIC, savelocale);
2995 g_object_set(renderer, "text", buf, NULL);
3001 GtkWidget* create_list(user_data_t* user_data)
3004 GtkListStore *list_store;
3006 GtkTreeViewColumn *column;
3007 GtkCellRenderer *renderer;
3008 GtkTreeSortable *sortable;
3009 GtkTreeView *list_view;
3010 GtkTreeSelection *selection;
3012 /* Create the store */
3013 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
3014 G_TYPE_UINT, /* Packet */
3015 G_TYPE_FLOAT, /* Delta(ms) */
3016 G_TYPE_FLOAT, /* Jitter(ms) */
3017 G_TYPE_FLOAT, /* IP BW(kbps) */
3018 G_TYPE_STRING, /* Status */
3019 G_TYPE_STRING, /* Date */
3020 G_TYPE_UINT, /* Length */
3021 G_TYPE_STRING, /* Foreground color */
3022 G_TYPE_STRING); /* Background color */
3025 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3027 list_view = GTK_TREE_VIEW(list);
3028 sortable = GTK_TREE_SORTABLE(list_store);
3030 #if GTK_CHECK_VERSION(2,6,0)
3031 /* Speed up the list display */
3032 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3035 /* Setup the sortable columns */
3036 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3037 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3039 /* The view now holds a reference. We can get rid of our own reference */
3040 g_object_unref (G_OBJECT (list_store));
3043 * Create the first column packet, associating the "text" attribute of the
3044 * cell_renderer to the first column of the model
3046 renderer = gtk_cell_renderer_text_new ();
3047 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3048 "text", PACKET_COLUMN,
3049 "foreground", FOREGROUND_COLOR_COL,
3050 "background", BACKGROUND_COLOR_COL,
3052 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3053 gtk_tree_view_column_set_resizable(column, TRUE);
3054 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3055 gtk_tree_view_column_set_min_width(column, 100);
3057 /* Add the column to the view. */
3058 gtk_tree_view_append_column (list_view, column);
3060 /* Second column.. Delta(ms). */
3061 renderer = gtk_cell_renderer_text_new ();
3062 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3063 "text", DELTA_COLUMN,
3064 "foreground", FOREGROUND_COLOR_COL,
3065 "background", BACKGROUND_COLOR_COL,
3068 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3069 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3071 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3072 gtk_tree_view_column_set_resizable(column, TRUE);
3073 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3074 gtk_tree_view_column_set_min_width(column, 100);
3075 gtk_tree_view_append_column (list_view, column);
3077 /* Third column.. Jitter(ms). */
3078 renderer = gtk_cell_renderer_text_new ();
3079 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3080 "text", JITTER_COLUMN,
3081 "foreground", FOREGROUND_COLOR_COL,
3082 "background", BACKGROUND_COLOR_COL,
3085 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3086 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3088 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3089 gtk_tree_view_column_set_resizable(column, TRUE);
3090 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3091 gtk_tree_view_column_set_min_width(column, 100);
3092 gtk_tree_view_append_column (list_view, column);
3094 /* Fourth column.. IP BW(kbps). */
3095 renderer = gtk_cell_renderer_text_new ();
3096 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3097 "text", IPBW_COLUMN,
3098 "foreground", FOREGROUND_COLOR_COL,
3099 "background", BACKGROUND_COLOR_COL,
3102 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3103 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3105 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3106 gtk_tree_view_column_set_resizable(column, TRUE);
3107 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3108 gtk_tree_view_column_set_min_width(column, 100);
3109 gtk_tree_view_append_column (list_view, column);
3111 /* Fifth column.. Status. */
3112 renderer = gtk_cell_renderer_text_new ();
3113 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3114 "text", STATUS_COLUMN,
3115 "foreground", FOREGROUND_COLOR_COL,
3116 "background", BACKGROUND_COLOR_COL,
3118 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3119 gtk_tree_view_column_set_resizable(column, TRUE);
3120 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3121 gtk_tree_view_column_set_min_width(column, 100);
3122 gtk_tree_view_append_column (list_view, column);
3124 /* Sixth column.. Length. */
3125 renderer = gtk_cell_renderer_text_new ();
3126 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3127 "text", LENGTH_COLUMN,
3128 "foreground", FOREGROUND_COLOR_COL,
3129 "background", BACKGROUND_COLOR_COL,
3133 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3134 gtk_tree_view_column_set_resizable(column, TRUE);
3135 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3136 gtk_tree_view_column_set_min_width(column, 100);
3137 gtk_tree_view_append_column (list_view, column);
3139 /* Now enable the sorting of each column */
3140 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3141 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3143 /* Setup the selection handler */
3144 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3145 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3147 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3148 G_CALLBACK (on_list_select_row),
3155 /****************************************************************************/
3156 /* Create the dialog box with all widgets */
3157 static void create_iax2_dialog(user_data_t* user_data)
3159 GtkWidget *window = NULL;
3160 GtkWidget *list_fwd;
3161 GtkWidget *list_rev;
3162 GtkWidget *label_stats_fwd;
3163 GtkWidget *label_stats_rev;
3164 GtkWidget *notebook;
3166 GtkWidget *main_vb, *page, *page_r;
3168 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3169 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3170 #ifdef USE_CONVERSATION_GRAPH
3171 GtkWidget *graph_bt;
3173 GtkWidget *graph_bt;
3174 gchar label_forward[150];
3175 gchar label_reverse[150];
3177 gchar str_ip_src[16];
3178 gchar str_ip_dst[16];
3180 window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
3181 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3183 /* Container for each row of widgets */
3184 main_vb = gtk_vbox_new(FALSE, 2);
3185 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3186 gtk_container_add(GTK_CONTAINER(window), main_vb);
3187 gtk_widget_show(main_vb);
3190 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3191 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3193 g_snprintf(label_forward, sizeof(label_forward),
3194 "Analysing stream from %s port %u to %s port %u ",
3195 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3198 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3199 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3201 g_snprintf(label_reverse, sizeof(label_reverse),
3202 "Analysing stream from %s port %u to %s port %u ",
3203 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3205 /* Start a notebook for flipping between sets of changes */
3206 notebook = gtk_notebook_new();
3207 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3208 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3210 user_data->dlg.notebook_signal_id =
3211 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3213 /* page for forward connection */
3214 page = gtk_vbox_new(FALSE, 8);
3215 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3217 /* direction label */
3218 label = gtk_label_new(label_forward);
3219 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3221 /* place for some statistics */
3222 label_stats_fwd = gtk_label_new("\n");
3223 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3225 /* scrolled window */
3226 scrolled_window = scrolled_window_new(NULL, NULL);
3229 list_fwd = create_list(user_data);
3230 gtk_widget_show(list_fwd);
3231 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3232 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3233 gtk_widget_show(scrolled_window);
3236 label = gtk_label_new(" Forward Direction ");
3237 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3239 /* same page for reversed connection */
3240 page_r = gtk_vbox_new(FALSE, 8);
3241 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3242 label = gtk_label_new(label_reverse);
3243 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3244 label_stats_rev = gtk_label_new("\n");
3245 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3247 scrolled_window_r = scrolled_window_new(NULL, NULL);
3249 list_rev = create_list(user_data);
3250 gtk_widget_show(list_rev);
3251 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3252 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3253 gtk_widget_show(scrolled_window_r);
3255 label = gtk_label_new(" Reversed Direction ");
3256 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3259 /* page for help&about or future */
3260 page_help = gtk_hbox_new(FALSE, 5);
3261 label = gtk_label_new(" Future ");
3262 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3263 frame = gtk_frame_new("");
3264 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3265 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3266 gtk_container_add(GTK_CONTAINER(frame), text);
3267 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3268 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3271 /* show all notebooks */
3272 gtk_widget_show_all(notebook);
3275 box4 = gtk_hbutton_box_new();
3276 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3277 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3278 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3279 gtk_box_set_spacing(GTK_BOX (box4), 0);
3280 gtk_widget_show(box4);
3282 voice_bt = gtk_button_new_with_label("Save payload...");
3283 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3284 gtk_widget_show(voice_bt);
3285 g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
3287 csv_bt = gtk_button_new_with_label("Save as CSV...");
3288 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3289 gtk_widget_show(csv_bt);
3290 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3292 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3293 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3294 gtk_widget_show(refresh_bt);
3295 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3297 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3298 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3299 gtk_widget_show(goto_bt);
3300 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3302 graph_bt = gtk_button_new_with_label("Graph");
3303 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3304 gtk_widget_show(graph_bt);
3305 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3308 #ifdef USE_CONVERSATION_GRAPH
3309 graph_bt = gtk_button_new_with_label("Graph");
3310 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3311 gtk_widget_show(graph_bt);
3312 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3315 next_bt = gtk_button_new_with_label("Next non-Ok");
3316 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3317 gtk_widget_show(next_bt);
3318 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3320 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3321 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3322 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3323 gtk_widget_show(close_bt);
3324 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3326 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3327 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3329 gtk_widget_show(window);
3330 window_present(window);
3332 /* some widget references need to be saved for outside use */
3333 user_data->dlg.window = window;
3334 user_data->dlg.list_fwd = list_fwd;
3335 user_data->dlg.list_rev = list_rev;
3336 user_data->dlg.label_stats_fwd = label_stats_fwd;
3337 user_data->dlg.label_stats_rev = label_stats_rev;
3338 user_data->dlg.notebook = notebook;
3339 user_data->dlg.selected_list = list_fwd;
3340 user_data->dlg.number_of_nok = 0;
3343 * select the initial row
3345 gtk_widget_grab_focus(list_fwd);
3349 /****************************************************************************/
3350 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3351 const gchar* proto_field, guint32* p_result)
3354 proto_node *proto_sibling_node;
3355 header_field_info *hfssrc;
3358 finfo = PITEM_FINFO(ptree_node);
3360 if (hfinformation==(finfo->hfinfo)) {
3361 hfssrc = proto_registrar_get_byname(proto_field);
3362 if (hfssrc == NULL) {
3363 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3367 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3368 ptree_node=ptree_node->next) {
3369 finfo=PITEM_FINFO(ptree_node);
3370 if (hfssrc==finfo->hfinfo) {
3371 if (hfinformation->type==FT_IPv4) {
3372 ipv4 = fvalue_get(&finfo->value);
3373 *p_result = ipv4_get_net_order_addr(ipv4);
3376 *p_result = fvalue_get_uinteger(&finfo->value);
3385 proto_sibling_node = ptree_node->next;
3387 if (proto_sibling_node) {
3388 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3394 /****************************************************************************/
3395 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3396 const gchar* proto_name,
3397 const gchar* proto_field,
3400 proto_node *ptree_node;
3401 header_field_info *hfinformation;
3403 hfinformation = proto_registrar_get_byname(proto_name);
3404 if (hfinformation == NULL) {
3405 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3410 ptree_node = ((proto_node *)protocol_tree)->first_child;
3412 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3416 return process_node(ptree_node, hfinformation, proto_field, p_result);
3420 /****************************************************************************/
3422 address *ip_src_fwd,
3423 guint16 port_src_fwd,
3424 address *ip_dst_fwd,
3425 guint16 port_dst_fwd,
3426 address *ip_src_rev,
3427 guint16 port_src_rev,
3428 address *ip_dst_rev,
3429 guint16 port_dst_rev
3432 user_data_t *user_data;
3435 static color_t col[MAX_GRAPHS] = {
3436 {0, 0x0000, 0x0000, 0x0000},
3437 {0, 0xffff, 0x0000, 0x0000},
3438 {0, 0x0000, 0xffff, 0x0000},
3439 {0, 0x0000, 0x0000, 0xffff}
3443 user_data = g_malloc(sizeof(user_data_t));
3445 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3446 user_data->port_src_fwd = port_src_fwd;
3447 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3448 user_data->port_dst_fwd = port_dst_fwd;
3449 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3450 user_data->port_src_rev = port_src_rev;
3451 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3452 user_data->port_dst_rev = port_dst_rev;
3455 /* file names for storing sound data */
3456 /*XXX: check for errors*/
3457 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3460 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3463 user_data->forward.saveinfo.fp = NULL;
3464 user_data->reversed.saveinfo.fp = NULL;
3465 user_data->dlg.save_voice_as_w = NULL;
3466 user_data->dlg.save_csv_as_w = NULL;
3467 user_data->dlg.dialog_graph.window = NULL;
3469 #ifdef USE_CONVERSATION_GRAPH
3470 user_data->dlg.graph_window = NULL;
3471 user_data->series_fwd.value_pairs = NULL;
3472 user_data->series_rev.value_pairs = NULL;
3475 /* init dialog_graph */
3476 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3477 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3478 user_data->dlg.dialog_graph.draw_area=NULL;
3479 user_data->dlg.dialog_graph.pixmap=NULL;
3480 user_data->dlg.dialog_graph.scrollbar=NULL;
3481 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3482 user_data->dlg.dialog_graph.pixmap_width=500;
3483 user_data->dlg.dialog_graph.pixmap_height=200;
3484 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3485 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3486 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3487 user_data->dlg.dialog_graph.max_interval=0;
3488 user_data->dlg.dialog_graph.num_items=0;
3489 user_data->dlg.dialog_graph.start_time = -1;
3491 for(i=0;i<MAX_GRAPHS;i++){
3492 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3493 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3494 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3495 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3496 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3497 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3498 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3499 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3502 /* create the dialog box */
3503 create_iax2_dialog(user_data);
3505 /* proceed as if the Refresh button would have been pressed */
3506 on_refresh_bt_clicked(NULL, user_data);
3509 /****************************************************************************/
3510 /* entry point from main menu */
3511 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3514 guint16 port_src_fwd;
3516 guint16 port_dst_fwd;
3518 guint16 port_src_rev;
3520 guint16 port_dst_rev;
3521 /* unsigned int ptype; */
3523 gchar filter_text[256];
3526 epan_dissect_t *edt;
3529 gboolean frame_matched;
3531 GList *strinfo_list;
3532 GList *filtered_list = NULL;
3533 rtp_stream_info_t *strinfo;
3536 /* Try to compile the filter. */
3537 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3538 if (!dfilter_compile(filter_text, &sfcode)) {
3539 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3542 /* we load the current file into cf variable */
3544 fdata = cf->current_frame;
3546 /* we are on the selected frame now */
3548 return; /* if we exit here it's an error */
3550 /* dissect the current frame */
3551 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3552 cf->pd, fdata->cap_len, &err, &err_info)) {
3553 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3554 cf_read_error_message(err, err_info), cf->filename);
3557 edt = epan_dissect_new(TRUE, FALSE);
3558 epan_dissect_prime_dfilter(edt, sfcode);
3559 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3560 frame_matched = dfilter_apply_edt(sfcode, edt);
3562 /* if it is not an iax2 frame, show an error dialog */
3563 frame_matched = dfilter_apply_edt(sfcode, edt);
3564 if (frame_matched != 1) {
3565 epan_dissect_free(edt);
3566 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3567 "You didn't choose a IAX2 packet!");
3570 /* check if it is Voice or MiniPacket
3571 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3572 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3573 "Please select a Voice packet!");
3577 /* check if it is part of a Call */
3578 if (edt->pi.circuit_id == 0) {
3579 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3580 "Please select a Call packet!");
3584 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3585 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3586 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3587 port_src_fwd = edt->pi.srcport;
3588 port_dst_fwd = edt->pi.destport;
3590 /* assume the inverse ip/port combination for the reverse direction */
3591 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3592 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3593 port_src_rev = edt->pi.destport;
3594 port_dst_rev = edt->pi.srcport;
3596 /* Scan for rtpstream */
3598 /* search for reversed direction in the global rtp streams list */
3600 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3601 while (strinfo_list)
3603 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3604 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3605 && strinfo->src_port==port_src_fwd
3606 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3607 && strinfo->dest_port==port_dst_fwd)
3609 filtered_list = g_list_prepend(filtered_list, strinfo);
3612 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3613 && strinfo->src_port==port_src_rev
3614 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3615 && strinfo->dest_port==port_dst_rev)
3618 filtered_list = g_list_append(filtered_list, strinfo);
3621 strinfo_list = g_list_next(strinfo_list);
3624 /* if more than one reverse streams found, we let the user choose the right one */
3626 rtpstream_dlg_show(filtered_list);
3643 /****************************************************************************/
3645 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3647 iax2_analysis_cb(NULL, NULL);
3650 /****************************************************************************/
3652 register_tap_listener_iax2_analysis(void)
3654 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3656 register_stat_menu_item("IAX2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3657 iax2_analysis_cb, NULL, NULL, NULL);
3667 * indent-tabs-mode: t
3670 * ex: set shiftwidth=8 tabstop=8 noexpandtab
3671 * :indentSize=8:tabSize=8:noTabs=false: