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 _U_);
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 _U_)
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 /* destroy save_voice_as window if open */
762 if (user_data->dlg.save_voice_as_w != NULL)
763 window_destroy(user_data->dlg.save_voice_as_w);
765 /* destroy graph window if open */
766 if (user_data->dlg.dialog_graph.window != NULL)
767 window_destroy(user_data->dlg.dialog_graph.window);
769 #ifdef USE_CONVERSATION_GRAPH
770 /* destroy graph window if open */
771 if (user_data->dlg.graph_window != NULL)
772 window_destroy(user_data->dlg.graph_window);
775 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
776 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
782 /****************************************************************************/
783 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
784 GtkNotebookPage *page _U_,
786 user_data_t *user_data _U_)
788 user_data->dlg.selected_list =
789 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
791 user_data->dlg.selected_list_row = 0;
794 /****************************************************************************/
795 static void on_list_select_row(GtkTreeSelection *selection,
796 user_data_t *user_data _U_/*gpointer data */)
798 user_data->dlg.selected_list_sel = selection;
802 #ifdef USE_CONVERSATION_GRAPH
803 Note this will not work any more as clist is removed.
804 /****************************************************************************/
805 /* when the graph window gets destroyed */
806 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
808 /* note that graph window has been destroyed */
809 user_data->dlg.graph_window = NULL;
812 /****************************************************************************/
813 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
816 GtkCList *clist = NULL;
817 if (vp.fnumber != 0) {
818 clist = GTK_CLIST(user_data->dlg.clist_fwd);
819 row = gtk_clist_find_row_from_data(clist,
820 GUINT_TO_POINTER(vp.fnumber));
822 clist = GTK_CLIST(user_data->dlg.clist_rev);
823 row = gtk_clist_find_row_from_data(clist,
824 GUINT_TO_POINTER(vp.fnumber));
827 gtk_notebook_set_current_page(GTK_NOTEBOOK(user_data->dlg.notebook),
828 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
829 gtk_clist_select_row(clist, row, 0);
830 gtk_clist_moveto(clist, row, 0, 0.5, 0);
836 /****************************************************************************/
837 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
843 if (user_data->dlg.graph_window != NULL) {
844 /* There's already a graph window; reactivate it. */
845 reactivate_window(user_data->dlg.graph_window);
848 list = g_list_append(list, &(user_data->series_fwd));
849 list = g_list_append(list, &(user_data->series_rev));
851 user_data->series_fwd.color.pixel = 0;
852 user_data->series_fwd.color.red = 0x80ff;
853 user_data->series_fwd.color.green = 0xe0ff;
854 user_data->series_fwd.color.blue = 0xffff;
855 user_data->series_fwd.yvalue = 0.5;
857 user_data->series_rev.color.pixel = 0;
858 user_data->series_rev.color.red = 0x60ff;
859 user_data->series_rev.color.green = 0xc0ff;
860 user_data->series_rev.color.blue = 0xffff;
861 user_data->series_rev.yvalue = -0.5;
863 g_snprintf(title1, sizeof(title1), "Forward: %s:%u to %s:%u",
864 get_addr_name(&(user_data->ip_src_fwd)),
865 user_data->port_src_fwd,
866 get_addr_name(&(user_data->ip_dst_fwd)),
867 user_data->port_dst_fwd);
869 g_snprintf(title2, sizeof(title2), "Reverse: %s:%u to %s:%u",
870 get_addr_name(&(user_data->ip_src_rev)),
871 user_data->port_src_rev,
872 get_addr_name(&(user_data->ip_dst_rev)),
873 user_data->port_dst_rev);
875 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
876 &graph_selection_callback, user_data);
877 g_signal_connect(user_data->dlg.graph_window, "destroy",
878 G_CALLBACK(on_destroy_graph), user_data);
880 #endif /*USE_CONVERSATION_GRAPH*/
882 /****************************************************************************/
883 static void dialog_graph_set_title(user_data_t* user_data)
886 if (!user_data->dlg.dialog_graph.window){
889 title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
890 get_addr_name(&(user_data->ip_src_fwd)),
891 user_data->port_src_fwd,
892 get_addr_name(&(user_data->ip_dst_fwd)),
893 user_data->port_dst_fwd,
894 get_addr_name(&(user_data->ip_src_rev)),
895 user_data->port_src_rev,
896 get_addr_name(&(user_data->ip_dst_rev)),
897 user_data->port_dst_rev);
899 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
905 /****************************************************************************/
906 static void dialog_graph_reset(user_data_t* user_data)
910 user_data->dlg.dialog_graph.needs_redraw=TRUE;
911 for(i=0;i<MAX_GRAPHS;i++){
912 for(j=0;j<NUM_GRAPH_ITEMS;j++){
913 dialog_graph_graph_item_t *dggi;
914 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
919 user_data->dlg.dialog_graph.last_interval=0xffffffff;
920 user_data->dlg.dialog_graph.max_interval=0;
921 user_data->dlg.dialog_graph.num_items=0;
923 /* create the color titles near the filter buttons */
924 for(i=0;i<MAX_GRAPHS;i++){
927 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
928 sizeof (user_data->dlg.dialog_graph.graph[0].title),
929 "%s: %s:%u to %s:%u",
931 get_addr_name(&(user_data->ip_src_fwd)),
932 user_data->port_src_fwd,
933 get_addr_name(&(user_data->ip_dst_fwd)),
934 user_data->port_dst_fwd);
937 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
938 sizeof(user_data->dlg.dialog_graph.graph[0].title),
939 "%s: %s:%u to %s:%u",
941 get_addr_name(&(user_data->ip_src_rev)),
942 user_data->port_src_rev,
943 get_addr_name(&(user_data->ip_dst_rev)),
944 user_data->port_dst_rev);
948 dialog_graph_set_title(user_data);
951 /****************************************************************************/
952 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
954 dialog_graph_graph_item_t *it;
961 /****************************************************************************/
962 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
965 g_snprintf(buf, buf_len, "%ds",t/1000000);
966 } else if(t>=1000000){
967 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
969 g_snprintf(buf, buf_len, "%dms",t/1000);
971 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
973 g_snprintf(buf, buf_len, "%dus",t);
977 /****************************************************************************/
978 static void dialog_graph_draw(user_data_t* user_data)
981 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
982 gint32 current_interval;
983 guint32 left_x_border;
984 guint32 right_x_border;
985 guint32 top_y_border;
986 guint32 bottom_y_border;
988 int label_width, label_height;
989 guint32 draw_width, draw_height;
990 char label_string[15];
993 guint32 num_time_intervals;
994 guint32 max_value; /* max value of seen data */
995 guint32 max_y; /* max value of the Y scale */
997 if(!user_data->dlg.dialog_graph.needs_redraw){
1000 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1003 * Find the length of the intervals we have data for
1004 * so we know how large arrays we need to malloc()
1006 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1007 /* if there isnt anything to do, just return */
1008 if(num_time_intervals==0){
1011 num_time_intervals+=1;
1012 /* XXX move this check to _packet() */
1013 if(num_time_intervals>NUM_GRAPH_ITEMS){
1014 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
1019 * find the max value so we can autoscale the y axis
1022 for(i=0;i<MAX_GRAPHS;i++){
1025 if(!user_data->dlg.dialog_graph.graph[i].display){
1028 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1031 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1033 /* keep track of the max value we have encountered */
1041 * Clear out old plot
1043 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1044 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1047 user_data->dlg.dialog_graph.draw_area->allocation.width,
1048 user_data->dlg.dialog_graph.draw_area->allocation.height);
1052 * Calculate the y scale we should use
1054 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1055 max_y=yscale_max[MAX_YSCALE-1];
1056 for(i=MAX_YSCALE-1;i>0;i--){
1057 if(max_value<yscale_max[i]){
1058 max_y=yscale_max[i];
1062 /* the user had specified an explicit y scale to use */
1063 max_y=user_data->dlg.dialog_graph.max_y_units;
1067 * Calculate size of borders surrounding the plot
1068 * The border on the right side needs to be adjusted depending
1069 * on the width of the text labels. For simplicity we assume that the
1070 * top y scale label will be the widest one
1072 print_time_scale_string(label_string, sizeof(label_string), max_y);
1073 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1074 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1076 right_x_border=label_width+20;
1078 bottom_y_border=label_height+20;
1082 * Calculate the size of the drawing area for the actual plot
1084 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1085 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1089 * Draw the y axis and labels
1090 * (we always draw the y scale with 11 ticks along the axis)
1092 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1093 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1095 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1096 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1102 /* first, middle and last tick are slightly longer */
1106 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1107 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1108 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1109 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1110 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1111 /* draw the labels */
1113 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1114 pango_layout_set_text(layout, label_string, -1);
1115 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1116 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1117 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1118 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1119 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1123 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1124 pango_layout_set_text(layout, label_string, -1);
1125 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1126 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1127 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1128 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1129 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1133 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1134 pango_layout_set_text(layout, label_string, -1);
1135 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1136 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1137 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1138 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1139 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1147 * if we have not specified the last_interval via the gui,
1148 * then just pick the current end of the capture so that is scrolls
1149 * nicely when doing live captures
1151 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1152 last_interval=user_data->dlg.dialog_graph.max_interval;
1154 last_interval=user_data->dlg.dialog_graph.last_interval;
1161 /* plot the x-scale */
1162 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);
1164 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1165 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1166 first_interval*=user_data->dlg.dialog_graph.interval;
1173 while(interval_delta<((last_interval-first_interval)/10)){
1174 interval_delta*=delta_multiplier;
1175 if(delta_multiplier==5){
1182 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1185 /* if pixels_per_tick is <5, only draw every 10 ticks */
1186 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1190 if(current_interval%interval_delta){
1196 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1197 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1198 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1199 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1200 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1201 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1205 if(user_data->dlg.dialog_graph.interval>=1000){
1206 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1207 } else if(user_data->dlg.dialog_graph.interval>=100){
1208 g_snprintf(label_string, sizeof(label_string),
1209 "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1210 } else if(user_data->dlg.dialog_graph.interval>=10){
1211 g_snprintf(label_string, sizeof(label_string),
1212 "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1214 g_snprintf(label_string, sizeof(label_string),
1215 "%d.%3ds", current_interval/1000,current_interval%1000);
1217 pango_layout_set_text(layout, label_string, -1);
1218 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1219 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1220 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1221 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1222 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1234 * Draw "x" for Sequence Errors and "m" for Marks
1236 /* Draw the labels Fwd and Rev */
1237 g_strlcpy(label_string, "<-Fwd", sizeof(label_string));
1238 pango_layout_set_text(layout, label_string, -1);
1239 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1240 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1241 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1242 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1243 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1245 g_strlcpy(label_string, "<-Rev", sizeof(label_string));
1246 pango_layout_set_text(layout, label_string, -1);
1247 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1248 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1249 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1250 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1251 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1254 /* Draw the marks */
1255 for(i=MAX_GRAPHS-1;i>=0;i--){
1257 guint32 x_pos, prev_x_pos;
1259 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1260 if (!user_data->dlg.dialog_graph.graph[i].display){
1263 /* initialize prev x/y to the low left corner of the graph */
1264 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;
1266 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1267 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;
1269 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1271 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1272 g_strlcpy(label_string, "x", sizeof(label_string));
1274 g_strlcpy(label_string, "m", sizeof(label_string));
1277 pango_layout_set_text(layout, label_string, -1);
1278 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1279 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1280 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1282 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1290 g_object_unref(G_OBJECT(layout));
1293 * Loop over all graphs and draw them
1295 for(i=MAX_GRAPHS-1;i>=0;i--){
1297 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1298 if (!user_data->dlg.dialog_graph.graph[i].display){
1301 /* initialize prev x/y to the low left corner of the graph */
1302 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;
1303 prev_y_pos=draw_height-1+top_y_border;
1305 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1307 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;
1308 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1312 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1315 /* dont need to draw anything if the segment
1316 * is entirely above the top of the graph
1318 if( (prev_y_pos==0) && (y_pos==0) ){
1325 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1326 x_pos, draw_height-1+top_y_border,
1336 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1337 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1338 user_data->dlg.dialog_graph.pixmap,
1341 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1344 /* update the scrollbar */
1345 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1346 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1347 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1348 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1349 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1351 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1353 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1354 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1355 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1359 /****************************************************************************/
1360 static void dialog_graph_redraw(user_data_t* user_data)
1362 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1363 dialog_graph_draw(user_data);
1366 /****************************************************************************/
1367 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1369 user_data_t *user_data;
1371 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1373 user_data->dlg.dialog_graph.window = NULL;
1377 /****************************************************************************/
1378 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1380 user_data_t *user_data;
1382 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1388 gdk_draw_pixmap(widget->window,
1389 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1390 user_data->dlg.dialog_graph.pixmap,
1391 event->area.x, event->area.y,
1392 event->area.x, event->area.y,
1393 event->area.width, event->area.height);
1398 /****************************************************************************/
1399 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1401 user_data_t *user_data;
1404 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1410 if(user_data->dlg.dialog_graph.pixmap){
1411 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1412 user_data->dlg.dialog_graph.pixmap=NULL;
1415 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1416 widget->allocation.width,
1417 widget->allocation.height,
1419 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1420 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1422 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1423 widget->style->white_gc,
1426 widget->allocation.width,
1427 widget->allocation.height);
1429 /* set up the colors and the GC structs for this pixmap */
1430 for(i=0;i<MAX_GRAPHS;i++){
1431 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1432 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1435 dialog_graph_redraw(user_data);
1439 /****************************************************************************/
1440 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1442 user_data_t *user_data=(user_data_t *)data;
1445 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1446 if(user_data->dlg.dialog_graph.last_interval==mi){
1449 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1450 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1454 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1456 dialog_graph_redraw(user_data);
1460 /****************************************************************************/
1461 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1463 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1464 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1465 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1467 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);
1469 /* signals needed to handle backing pixmap */
1470 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1471 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1473 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1474 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1476 /* create the associated scrollbar */
1477 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1478 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1479 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1480 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1481 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1484 /****************************************************************************/
1485 static void disable_graph(dialog_graph_graph_t *dgg)
1489 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1494 /****************************************************************************/
1495 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1497 /* this graph is not active, just update display and redraw */
1498 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1500 dialog_graph_redraw(dgg->ud);
1505 cf_retap_packets(&cfile, FALSE);
1506 dialog_graph_redraw(dgg->ud);
1511 /****************************************************************************/
1512 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1518 hbox=gtk_hbox_new(FALSE, 3);
1519 gtk_container_add(GTK_CONTAINER(box), hbox);
1520 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1521 gtk_widget_show(hbox);
1523 g_snprintf(str, sizeof(str), "Graph %d", num);
1524 dgg->display_button=gtk_toggle_button_new_with_label(str);
1525 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1526 gtk_widget_show(dgg->display_button);
1527 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1528 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1530 label=gtk_label_new(dgg->title);
1531 gtk_widget_show(label);
1532 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1534 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1535 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1536 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1537 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1538 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1543 /****************************************************************************/
1544 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1551 frame=gtk_frame_new("Graphs");
1552 gtk_container_add(GTK_CONTAINER(box), frame);
1553 gtk_widget_show(frame);
1555 vbox=gtk_vbox_new(FALSE, 1);
1556 gtk_container_add(GTK_CONTAINER(frame), vbox);
1557 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1558 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1559 gtk_widget_show(vbox);
1561 for(i=0;i<MAX_GRAPHS;i++){
1562 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1565 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1566 gtk_widget_show(label);
1567 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1572 /****************************************************************************/
1573 static void yscale_select(GtkWidget *item, gpointer key)
1576 user_data_t *user_data;
1578 user_data=(user_data_t *)key;
1579 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1581 user_data->dlg.dialog_graph.max_y_units=val;
1582 dialog_graph_redraw(user_data);
1585 /****************************************************************************/
1586 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1589 user_data_t *user_data;
1591 user_data=(user_data_t *)key;
1592 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1593 user_data->dlg.dialog_graph.pixels_per_tick=val;
1594 dialog_graph_redraw(user_data);
1597 /****************************************************************************/
1598 static void tick_interval_select(GtkWidget *item, gpointer key)
1601 user_data_t *user_data;
1603 user_data=(user_data_t *)key;
1604 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1606 user_data->dlg.dialog_graph.interval=val;
1607 cf_retap_packets(&cfile, FALSE);
1608 dialog_graph_redraw(user_data);
1611 /****************************************************************************/
1612 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1615 GtkWidget *menu_item;
1618 for(i=0;i<MAX_YSCALE;i++){
1619 if(yscale_max[i]==AUTO_MAX_YSCALE){
1620 g_strlcpy(str,"Auto",15);
1622 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1624 menu_item=gtk_menu_item_new_with_label(str);
1625 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1626 GUINT_TO_POINTER(yscale_max[i]));
1627 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1628 gtk_widget_show(menu_item);
1629 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1634 /****************************************************************************/
1635 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1638 GtkWidget *menu_item;
1641 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1642 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1643 menu_item=gtk_menu_item_new_with_label(str);
1645 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1646 GUINT_TO_POINTER(pixels_per_tick[i]));
1647 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1648 gtk_widget_show(menu_item);
1649 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1651 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1656 /****************************************************************************/
1657 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1660 GtkWidget *menu_item;
1663 for(i=0;i<MAX_TICK_VALUES;i++){
1664 if(tick_interval_values[i]>=1000){
1665 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1666 } else if(tick_interval_values[i]>=100){
1667 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1668 } else if(tick_interval_values[i]>=10){
1669 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1671 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1674 menu_item=gtk_menu_item_new_with_label(str);
1675 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1676 GUINT_TO_POINTER(tick_interval_values[i]));
1677 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1678 gtk_widget_show(menu_item);
1679 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1681 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1685 /****************************************************************************/
1686 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1690 GtkWidget *option_menu;
1693 hbox=gtk_hbox_new(FALSE, 0);
1694 gtk_container_add(GTK_CONTAINER(box), hbox);
1695 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1696 gtk_widget_show(hbox);
1698 label=gtk_label_new(name);
1699 gtk_widget_show(label);
1700 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1702 option_menu=gtk_option_menu_new();
1703 menu=gtk_menu_new();
1704 (*func)(user_data, menu);
1705 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1706 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1707 gtk_widget_show(option_menu);
1710 /****************************************************************************/
1711 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1713 GtkWidget *frame_vbox;
1717 frame_vbox=gtk_vbox_new(FALSE, 0);
1718 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1719 gtk_widget_show(frame_vbox);
1721 frame = gtk_frame_new("X Axis");
1722 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1723 gtk_widget_show(frame);
1725 vbox=gtk_vbox_new(FALSE, 0);
1726 gtk_container_add(GTK_CONTAINER(frame), vbox);
1727 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1728 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1729 gtk_widget_show(vbox);
1731 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1732 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1734 frame = gtk_frame_new("Y Axis");
1735 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1736 gtk_widget_show(frame);
1738 vbox=gtk_vbox_new(FALSE, 0);
1739 gtk_container_add(GTK_CONTAINER(frame), vbox);
1740 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1741 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1742 gtk_widget_show(vbox);
1744 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1749 /****************************************************************************/
1750 static void dialog_graph_init_window(user_data_t* user_data)
1754 GtkWidget *bt_close;
1756 /* create the main window */
1757 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1759 vbox=gtk_vbox_new(FALSE, 0);
1760 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1761 gtk_widget_show(vbox);
1763 create_draw_area(user_data, vbox);
1765 hbox=gtk_hbox_new(FALSE, 3);
1766 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1767 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1768 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1769 gtk_widget_show(hbox);
1771 create_filter_area(user_data, hbox);
1772 create_ctrl_area(user_data, hbox);
1774 dialog_graph_set_title(user_data);
1776 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1777 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1778 gtk_widget_show(hbox);
1780 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1781 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1783 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1785 gtk_widget_show(user_data->dlg.dialog_graph.window);
1786 window_present(user_data->dlg.dialog_graph.window);
1791 /****************************************************************************/
1792 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1794 if (user_data->dlg.dialog_graph.window != NULL) {
1795 /* There's already a graph window; reactivate it. */
1796 reactivate_window(user_data->dlg.dialog_graph.window);
1800 dialog_graph_init_window(user_data);
1804 /****************************************************************************/
1805 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1808 GtkTreeModel *model;
1809 GtkTreeSelection *selection;
1812 selection = user_data->dlg.selected_list_sel;
1814 if (selection==NULL)
1817 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1818 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1819 cf_goto_frame(&cfile, fnumber);
1824 static void draw_stat(user_data_t *user_data);
1826 /****************************************************************************/
1827 /* re-dissects all packets */
1828 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1830 GString *error_string;
1832 /* remove tap listener */
1833 protect_thread_critical_region();
1834 remove_tap_listener(user_data);
1835 unprotect_thread_critical_region();
1837 /* register tap listener */
1838 error_string = register_tap_listener("IAX2", user_data, NULL,
1839 iax2_reset, iax2_packet, iax2_draw);
1840 if (error_string != NULL) {
1841 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1842 g_string_free(error_string, TRUE);
1846 /* retap all packets */
1847 cf_retap_packets(&cfile, FALSE);
1849 /* draw statistics info */
1850 draw_stat(user_data);
1854 /****************************************************************************/
1855 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1858 GtkTreeModel *model;
1860 GtkTreeSelection *selection;
1863 selection = user_data->dlg.selected_list_sel;
1865 if (selection==NULL)
1869 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1870 while (gtk_tree_model_iter_next (model,&iter)) {
1871 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1872 if (strcmp(text, OK_TEXT) != 0) {
1873 gtk_tree_selection_select_iter (selection, &iter);
1874 path = gtk_tree_model_get_path(model, &iter);
1875 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1878 gtk_tree_path_free(path);
1885 if (user_data->dlg.number_of_nok>1){
1886 /* Get the first iter and select it before starting over */
1887 gtk_tree_model_get_iter_first(model, &iter);
1888 gtk_tree_selection_select_iter (selection, &iter);
1894 /****************************************************************************/
1895 /* when we want to save the information */
1896 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1899 GtkWidget *rev, *forw, *both;
1900 user_data_t *user_data;
1902 GtkListStore *store;
1904 GtkTreeSelection *selection;
1905 GtkTreeModel *model;
1907 /* To Hold data from the list row */
1908 guint packet; /* Packet */
1909 gfloat delta; /* Delta(ms) */
1910 gfloat jitter; /* Jitter(ms) */
1911 gfloat ipbw; /* IP BW(kbps) */
1912 char * status_str; /* Status */
1913 char * date_str; /* Date */
1914 guint length; /* Length */
1920 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1922 /* Perhaps the user specified a directory instead of a file.
1923 * Check whether they did.
1925 if (test_for_directory(g_dest) == EISDIR) {
1926 /* It's a directory - set the file selection box to display it. */
1927 set_last_open_dir(g_dest);
1929 file_selection_set_current_folder(fs, get_last_open_dir());
1933 rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1934 forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1935 both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1936 user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1938 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1939 fp = ws_fopen(g_dest, "w");
1941 open_failure_alert_box(g_dest, errno, TRUE);
1945 if (GTK_TOGGLE_BUTTON(both)->active) {
1946 fprintf(fp, "Forward\n");
1948 write_failure_alert_box(g_dest, errno);
1954 for(j = 0; j < NUM_COLS; j++) {
1956 fprintf(fp,"%s",titles[j]);
1958 fprintf(fp,",%s",titles[j]);
1963 write_failure_alert_box(g_dest, errno);
1967 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1968 store = GTK_LIST_STORE(model);
1969 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1971 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1973 while (gtk_tree_model_iter_next (model,&iter)) {
1974 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1983 fprintf(fp, "%u",packet);
1984 fprintf(fp, ",%.2f", delta);
1985 fprintf(fp, ",%.2f", jitter);
1986 fprintf(fp, ",%.2f", ipbw);
1987 fprintf(fp, ",%s", status_str);
1988 fprintf(fp, ",%s", date_str);
1989 fprintf(fp, ",%u", length);
1993 write_failure_alert_box(g_dest, errno);
1999 if (fclose(fp) == EOF) {
2000 write_failure_alert_box(g_dest, errno);
2005 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2007 if (GTK_TOGGLE_BUTTON(both)->active) {
2008 fp = ws_fopen(g_dest, "a");
2010 open_failure_alert_box(g_dest, errno, TRUE);
2013 fprintf(fp, "\nReverse\n");
2015 write_failure_alert_box(g_dest, errno);
2020 fp = ws_fopen(g_dest, "w");
2022 open_failure_alert_box(g_dest, errno, TRUE);
2026 for(j = 0; j < NUM_COLS; j++) {
2028 fprintf(fp,"%s",titles[j]);
2030 fprintf(fp,",%s",titles[j]);
2035 write_failure_alert_box(g_dest, errno);
2039 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2040 store = GTK_LIST_STORE(model);
2041 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2043 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_rev));
2045 while (gtk_tree_model_iter_next (model,&iter)) {
2046 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2055 fprintf(fp, "%u",packet);
2056 fprintf(fp, ",%.2f", delta);
2057 fprintf(fp, ",%.2f", jitter);
2058 fprintf(fp, ",%.2f", ipbw);
2059 fprintf(fp, ",%s", status_str);
2060 fprintf(fp, ",%s", date_str);
2061 fprintf(fp, ",%u", length);
2065 write_failure_alert_box(g_dest, errno);
2070 if (fclose(fp) == EOF) {
2071 write_failure_alert_box(g_dest, errno);
2076 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2079 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2081 user_data->dlg.save_csv_as_w = NULL;
2084 /* when the user wants to save the csv information in a file */
2085 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2089 GtkWidget *label_format;
2090 GtkWidget *channels_label;
2091 GSList *channels_group = NULL;
2092 GtkWidget *forward_rb;
2093 GtkWidget *reversed_rb;
2097 if (user_data->dlg.save_csv_as_w != NULL) {
2098 /* There's already a Save CSV info dialog box; reactivate it. */
2099 reactivate_window(user_data->dlg.save_csv_as_w);
2103 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV", GTK_WINDOW(user_data->dlg.notebook), GTK_FILE_CHOOSER_ACTION_SAVE,
2104 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2105 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2108 /* Container for each row of widgets */
2109 vertb = gtk_vbox_new(FALSE, 0);
2110 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2111 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2112 gtk_widget_show (vertb);
2114 table1 = gtk_table_new (2, 4, FALSE);
2115 gtk_widget_show (table1);
2116 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2117 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2118 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2120 label_format = gtk_label_new ("Format: Comma Separated Values");
2121 gtk_widget_show (label_format);
2122 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2123 (GtkAttachOptions) (GTK_FILL),
2124 (GtkAttachOptions) (0), 0, 0);
2127 channels_label = gtk_label_new ("Channels:");
2128 gtk_widget_show (channels_label);
2129 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2130 (GtkAttachOptions) (GTK_FILL),
2131 (GtkAttachOptions) (0), 0, 0);
2132 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2134 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2135 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2136 gtk_widget_show (forward_rb);
2137 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2138 (GtkAttachOptions) (GTK_FILL),
2139 (GtkAttachOptions) (0), 0, 0);
2141 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2142 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2143 gtk_widget_show (reversed_rb);
2144 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2145 (GtkAttachOptions) (GTK_FILL),
2146 (GtkAttachOptions) (0), 0, 0);
2148 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2149 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2150 gtk_widget_show (both_rb);
2151 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2152 (GtkAttachOptions) (GTK_FILL),
2153 (GtkAttachOptions) (0), 0, 0);
2155 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2157 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2158 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2159 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2160 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2161 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2162 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_csv_as_ok_cb),
2163 user_data->dlg.save_csv_as_w);
2165 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2166 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, window_cancel_button_cb);
2168 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2169 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2170 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2172 gtk_widget_show(user_data->dlg.save_csv_as_w);
2173 window_present(user_data->dlg.save_csv_as_w);
2177 /****************************************************************************/
2178 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2180 /* Note that we no longer have a Save voice info dialog box. */
2181 user_data->dlg.save_voice_as_w = NULL;
2184 /****************************************************************************/
2185 /* here we save it into a file that user specified */
2186 /* XXX what about endians here? could go something wrong? */
2187 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2189 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2190 gchar f_pd[1] = {0};
2191 gchar r_pd[1] = {0};
2194 guint32 f_write_silence = 0;
2195 guint32 r_write_silence = 0;
2197 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2198 gboolean stop_flag = FALSE;
2201 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2204 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2210 /* open file for saving */
2211 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2218 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2220 if (format == SAVE_AU_FORMAT) /* au format */
2222 /* First we write the .au header. XXX Hope this is endian independant */
2223 /* the magic word 0x2e736e64 == .snd */
2224 phtonl(pd, 0x2e736e64);
2225 nchars=ws_write(to_fd, pd, 4);
2226 /* header offset == 24 bytes */
2228 nchars=ws_write(to_fd, pd, 4);
2229 /* total length, it is permited to set this to 0xffffffff */
2231 nchars=ws_write(to_fd, pd, 4);
2232 /* encoding format == 16-bit linear PCM */
2234 nchars=ws_write(to_fd, pd, 4);
2235 /* sample rate == 8000 Hz */
2237 nchars=ws_write(to_fd, pd, 4);
2240 nchars=ws_write(to_fd, pd, 4);
2244 /* only forward direction */
2245 case SAVE_FORWARD_DIRECTION_MASK: {
2246 progbar_count = user_data->forward.saveinfo.count;
2247 progbar_quantum = user_data->forward.saveinfo.count/100;
2248 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2251 if((count > progbar_nextstep) && (count <= progbar_count)) {
2252 update_progress_dlg(progbar,
2253 (gfloat) count/progbar_count, "Saving");
2254 progbar_nextstep = progbar_nextstep + progbar_quantum;
2258 if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
2259 sample = ulaw2linear(*f_pd);
2262 else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
2263 sample = alaw2linear(*f_pd);
2270 destroy_progress_dlg(progbar);
2274 fwritten = ws_write(to_fd, pd, 2);
2275 if ((fwritten < 2) || (fwritten < 0) || (fread < 0)) {
2279 destroy_progress_dlg(progbar);
2285 /* only reversed direction */
2286 case SAVE_REVERSE_DIRECTION_MASK: {
2287 progbar_count = user_data->reversed.saveinfo.count;
2288 progbar_quantum = user_data->reversed.saveinfo.count/100;
2289 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2292 if((count > progbar_nextstep) && (count <= progbar_count)) {
2293 update_progress_dlg(progbar,
2294 (gfloat) count/progbar_count, "Saving");
2295 progbar_nextstep = progbar_nextstep + progbar_quantum;
2299 if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
2300 sample = ulaw2linear(*r_pd);
2303 else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
2304 sample = alaw2linear(*r_pd);
2311 destroy_progress_dlg(progbar);
2315 rwritten = ws_write(to_fd, pd, 2);
2316 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2320 destroy_progress_dlg(progbar);
2326 /* both directions */
2327 case SAVE_BOTH_DIRECTION_MASK: {
2328 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2329 (progbar_count = user_data->forward.saveinfo.count) :
2330 (progbar_count = user_data->reversed.saveinfo.count);
2331 progbar_quantum = progbar_count/100;
2332 /* since conversation in one way can start later than in the other one,
2333 * we have to write some silence information for one channel */
2334 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2335 f_write_silence = (guint32)
2336 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2338 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2339 r_write_silence = (guint32)
2340 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2345 if((count > progbar_nextstep) && (count <= progbar_count)) {
2346 update_progress_dlg(progbar,
2347 (gfloat) count/progbar_count, "Saving");
2348 progbar_nextstep = progbar_nextstep + progbar_quantum;
2351 if(f_write_silence > 0) {
2352 rread = read(rev_fd, r_pd, 1);
2353 switch (user_data->forward.statinfo.reg_pt) {
2354 case AST_FORMAT_ULAW:
2355 *f_pd = SILENCE_PCMU;
2357 case AST_FORMAT_ALAW:
2358 *f_pd = SILENCE_PCMA;
2364 else if(r_write_silence > 0) {
2365 fread = read(forw_fd, f_pd, 1);
2366 switch (user_data->reversed.statinfo.reg_pt) {
2367 case AST_FORMAT_ULAW:
2368 *r_pd = SILENCE_PCMU;
2370 case AST_FORMAT_ALAW:
2371 *r_pd = SILENCE_PCMA;
2378 fread = read(forw_fd, f_pd, 1);
2379 rread = read(rev_fd, r_pd, 1);
2381 if ((rread == 0) && (fread == 0))
2383 if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
2384 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2387 else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
2388 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2396 destroy_progress_dlg(progbar);
2401 rwritten = ws_write(to_fd, pd, 2);
2402 if ((rwritten < 2) || (rread < 0) || (fread < 0)) {
2406 destroy_progress_dlg(progbar);
2413 else if (format == SAVE_RAW_FORMAT) /* raw format */
2417 /* only forward direction */
2418 case SAVE_FORWARD_DIRECTION_MASK: {
2419 progbar_count = user_data->forward.saveinfo.count;
2420 progbar_quantum = user_data->forward.saveinfo.count/100;
2424 /* only reversed direction */
2425 case SAVE_REVERSE_DIRECTION_MASK: {
2426 progbar_count = user_data->reversed.saveinfo.count;
2427 progbar_quantum = user_data->reversed.saveinfo.count/100;
2435 destroy_progress_dlg(progbar);
2442 /* XXX how do you just copy the file? */
2443 while ((rread = read(fd, pd, 1)) > 0) {
2446 if((count > progbar_nextstep) && (count <= progbar_count)) {
2447 update_progress_dlg(progbar,
2448 (gfloat) count/progbar_count, "Saving");
2449 progbar_nextstep = progbar_nextstep + progbar_quantum;
2453 rwritten = ws_write(to_fd, pd, 1);
2455 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2459 destroy_progress_dlg(progbar);
2465 destroy_progress_dlg(progbar);
2473 /****************************************************************************/
2474 /* the user wants to save in a file */
2475 /* XXX support for different formats is currently commented out */
2476 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2479 /*GtkWidget *wav, *sw;*/
2480 GtkWidget *au, *raw;
2481 GtkWidget *rev, *forw, *both;
2482 user_data_t *user_data;
2483 gint channels , format;
2485 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
2487 /* Perhaps the user specified a directory instead of a file.
2488 Check whether they did. */
2489 if (test_for_directory(g_dest) == EISDIR) {
2490 /* It's a directory - set the file selection box to display it. */
2491 set_last_open_dir(g_dest);
2493 file_selection_set_current_folder(fs, get_last_open_dir());
2497 /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2498 sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2499 au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2500 raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2501 rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2502 forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2503 both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2504 user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2506 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2507 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2508 * disable the ok button or disable the buttons for direction if only one is not ok. The
2509 * problem is if we open the save voice dialog and then click the refresh button and maybe
2510 * the state changes, so we can't save anymore. In this case we should be able to update
2511 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2514 /* we can not save in both directions */
2515 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2516 /* there are many combinations here, we just exit when first matches */
2517 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2518 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2519 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2520 "Can't save in a file: Unsupported codec!");
2521 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2522 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2523 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2524 "Can't save in a file: Wrong length of captured packets!");
2525 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2526 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2527 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2528 "Can't save in a file: Not all data in all packets was captured!");
2530 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2531 "Can't save in a file: File I/O problem!");
2534 /* we can not save forward direction */
2535 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2536 (GTK_TOGGLE_BUTTON (both)->active))) {
2537 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2538 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2539 "Can't save forward direction in a file: Unsupported codec!");
2540 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2541 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2542 "Can't save forward direction in a file: Wrong length of captured packets!");
2543 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2544 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2545 "Can't save forward direction in a file: Not all data in all packets was captured!");
2547 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2548 "Can't save forward direction in a file: File I/O problem!");
2551 /* we can not save reversed direction */
2552 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2553 (GTK_TOGGLE_BUTTON (both)->active))) {
2554 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2555 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2556 "Can't save reversed direction in a file: Unsupported codec!");
2557 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2558 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2559 "Can't save reversed direction in a file: Wrong length of captured packets!");
2560 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2561 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2562 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2563 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2564 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2565 "Can't save reversed direction in a file: No IAX2 data!");
2567 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2568 "Can't save reversed direction in a file: File I/O problem!");
2572 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2573 format = SAVE_WAV_FORMAT;
2574 else */if (GTK_TOGGLE_BUTTON (au)->active)
2575 format = SAVE_AU_FORMAT;
2576 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2577 format = SAVE_SW_FORMAT;*/
2578 else if (GTK_TOGGLE_BUTTON (raw)->active)
2579 format = SAVE_RAW_FORMAT;
2581 format = SAVE_NONE_FORMAT;
2583 if (GTK_TOGGLE_BUTTON (rev)->active)
2584 channels = SAVE_REVERSE_DIRECTION_MASK;
2585 else if (GTK_TOGGLE_BUTTON (both)->active)
2586 channels = SAVE_BOTH_DIRECTION_MASK;
2588 channels = SAVE_FORWARD_DIRECTION_MASK;
2590 /* direction/format validity*/
2591 if (format == SAVE_AU_FORMAT)
2593 /* make sure streams are alaw/ulaw */
2594 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
2595 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2596 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2599 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
2600 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2601 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2604 /* make sure pt's don't differ */
2605 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2606 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2607 "Can't save in a file: Forward and reverse direction differ in type");
2611 else if (format == SAVE_RAW_FORMAT)
2613 /* can't save raw in both directions */
2614 if (channels == SAVE_BOTH_DIRECTION_MASK){
2615 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2616 "Can't save in a file: Unable to save raw data in both directions");
2622 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2623 "Can't save in a file: Invalid save format");
2627 if(!copy_file(g_dest, channels, format, user_data)) {
2628 /* XXX - report the error type! */
2629 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2630 "An error occurred while saving voice in a file!");
2634 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2637 /****************************************************************************/
2638 /* when the user wants to save the voice information in a file */
2639 /* XXX support for different formats is currently commented out */
2640 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2644 GtkWidget *label_format;
2645 GtkWidget *channels_label;
2646 GSList *format_group = NULL;
2647 GSList *channels_group = NULL;
2648 GtkWidget *forward_rb;
2649 GtkWidget *reversed_rb;
2651 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2656 /* if we can't save in a file: wrong codec, cut packets or other errors */
2657 /* shold the error arise here or later when you click ok button ?
2658 * if we do it here, then we must disable the refresh button, so we don't do it here */
2660 if (user_data->dlg.save_voice_as_w != NULL) {
2661 /* There's already a Save voice info dialog box; reactivate it. */
2662 reactivate_window(user_data->dlg.save_voice_as_w);
2666 /* XXX - use file_selection from dlg_utils instead! */
2667 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...", GTK_WINDOW(user_data->dlg.notebook), GTK_FILE_CHOOSER_ACTION_SAVE,
2668 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2669 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2672 /* Container for each row of widgets */
2673 vertb = gtk_vbox_new(FALSE, 0);
2674 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2675 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2676 gtk_widget_show (vertb);
2678 table1 = gtk_table_new (2, 4, FALSE);
2679 gtk_widget_show (table1);
2680 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2681 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2682 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2685 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2686 gtk_widget_show (label_format);
2687 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2688 (GtkAttachOptions) (GTK_FILL),
2689 (GtkAttachOptions) (0), 0, 0);
2691 label_format = gtk_label_new ("Format: ");
2692 gtk_widget_show (label_format);
2693 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2694 (GtkAttachOptions) (GTK_FILL),
2695 (GtkAttachOptions) (0), 0, 0);
2697 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2699 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2700 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2701 gtk_widget_show (raw_rb);
2702 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2703 (GtkAttachOptions) (GTK_FILL),
2704 (GtkAttachOptions) (0), 0, 0);
2707 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2708 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2709 gtk_widget_show (au_rb);
2710 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2711 (GtkAttachOptions) (GTK_FILL),
2712 (GtkAttachOptions) (0), 0, 0);
2714 /* we support .au - ulaw*/
2716 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2717 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2718 gtk_widget_show (wav_rb);
2719 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2720 (GtkAttachOptions) (GTK_FILL),
2721 (GtkAttachOptions) (0), 0, 0);
2723 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2724 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2725 gtk_widget_show (sw_rb);
2726 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2727 (GtkAttachOptions) (GTK_FILL),
2728 (GtkAttachOptions) (0), 0, 0);
2729 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2730 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2731 gtk_widget_show (au_rb);
2732 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2733 (GtkAttachOptions) (GTK_FILL),
2734 (GtkAttachOptions) (0), 0, 0);
2738 channels_label = gtk_label_new ("Channels:");
2739 gtk_widget_show (channels_label);
2740 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2741 (GtkAttachOptions) (GTK_FILL),
2742 (GtkAttachOptions) (0), 0, 0);
2743 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2745 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2746 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2747 gtk_widget_show (forward_rb);
2748 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2749 (GtkAttachOptions) (GTK_FILL),
2750 (GtkAttachOptions) (0), 0, 0);
2752 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2753 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2754 gtk_widget_show (reversed_rb);
2755 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2756 (GtkAttachOptions) (GTK_FILL),
2757 (GtkAttachOptions) (0), 0, 0);
2759 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2760 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2761 gtk_widget_show (both_rb);
2762 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2763 (GtkAttachOptions) (GTK_FILL),
2764 (GtkAttachOptions) (0), 0, 0);
2766 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2769 /* if one direction is nok we don't allow saving
2770 XXX this is not ok since the user can click the refresh button and cause changes
2771 but we can not update this window. So we move all the decision on the time the ok
2772 button is clicked */
2773 if (user_data->forward.saved == FALSE) {
2774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2775 gtk_widget_set_sensitive(forward_rb, FALSE);
2776 gtk_widget_set_sensitive(both_rb, FALSE);
2778 else if (user_data->reversed.saved == FALSE) {
2779 gtk_widget_set_sensitive(reversed_rb, FALSE);
2780 gtk_widget_set_sensitive(both_rb, FALSE);
2784 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2785 /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2786 g_object_set_data(G_OBJECT(ok_bt), "au_rb", au_rb);
2787 /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2788 g_object_set_data(G_OBJECT(ok_bt), "raw_rb", raw_rb);
2789 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2790 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2791 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2792 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2793 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_voice_as_ok_cb),
2794 user_data->dlg.save_voice_as_w);
2796 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2797 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2799 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2800 G_CALLBACK(window_delete_event_cb), NULL);
2801 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2802 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2804 gtk_widget_show(user_data->dlg.save_voice_as_w);
2805 window_present(user_data->dlg.save_voice_as_w);
2809 /****************************************************************************/
2810 /* when we are finished with redisection, we add the label for the statistic */
2811 static void draw_stat(user_data_t *user_data)
2813 gchar label_max[200];
2815 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2816 user_data->forward.statinfo.total_nr,
2817 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
2819 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2821 g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
2822 user_data->reversed.statinfo.total_nr,
2823 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
2825 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2832 /****************************************************************************/
2833 /* append a line to list */
2834 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
2835 double delta, double jitter, double bandwidth, gchar *status,
2836 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2838 GtkListStore *list_store;
2840 if (strcmp(status, OK_TEXT) != 0) {
2841 user_data->dlg.number_of_nok++;
2844 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2846 /* Creates a new row at position. iter will be changed to point to this new row.
2847 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2848 * The row will be filled with the values given to this function.
2850 * should generally be preferred when inserting rows in a sorted list store.
2852 #if GTK_CHECK_VERSION(2,6,0)
2853 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2854 PACKET_COLUMN, number,
2855 DELTA_COLUMN, delta,
2856 JITTER_COLUMN, jitter,
2857 IPBW_COLUMN, bandwidth,
2858 STATUS_COLUMN, (char *)status,
2859 DATE_COLUMN, (char *)timeStr,
2860 LENGTH_COLUMN, pkt_len,
2861 FOREGROUND_COLOR_COL, NULL,
2862 BACKGROUND_COLOR_COL, (char *)color_str,
2866 gtk_list_store_append (list_store, &user_data->dlg.iter);
2867 gtk_list_store_set (list_store, &user_data->dlg.iter,
2868 PACKET_COLUMN, number,
2869 DELTA_COLUMN, delta,
2870 JITTER_COLUMN, jitter,
2871 IPBW_COLUMN, bandwidth,
2872 STATUS_COLUMN, (char *)status,
2873 DATE_COLUMN, (char *)timeStr,
2874 LENGTH_COLUMN, pkt_len,
2875 FOREGROUND_COLOR_COL, NULL,
2876 BACKGROUND_COLOR_COL, (char *)color_str,
2879 if(flags & STAT_FLAG_FIRST){
2880 /* Set first row as active */
2881 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2885 /****************************************************************************
2886 * Functions needed to present values from the list
2889 /* Present floats with two decimals */
2891 iax2_float_data_func (GtkTreeViewColumn *column _U_,
2892 GtkCellRenderer *renderer,
2893 GtkTreeModel *model,
2901 /* the col to get data from is in userdata */
2902 gint float_col = GPOINTER_TO_INT(user_data);
2904 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
2906 /* save the current locale */
2907 savelocale = setlocale(LC_NUMERIC, NULL);
2908 /* switch to "C" locale to avoid problems with localized decimal separators
2909 * in g_snprintf("%f") functions
2911 setlocale(LC_NUMERIC, "C");
2913 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
2914 /* restore previous locale setting */
2915 setlocale(LC_NUMERIC, savelocale);
2917 g_object_set(renderer, "text", buf, NULL);
2923 GtkWidget* create_list(user_data_t* user_data)
2926 GtkListStore *list_store;
2928 GtkTreeViewColumn *column;
2929 GtkCellRenderer *renderer;
2930 GtkTreeSortable *sortable;
2931 GtkTreeView *list_view;
2932 GtkTreeSelection *selection;
2934 /* Create the store */
2935 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2936 G_TYPE_UINT, /* Packet */
2937 G_TYPE_FLOAT, /* Delta(ms) */
2938 G_TYPE_FLOAT, /* Jitter(ms) */
2939 G_TYPE_FLOAT, /* IP BW(kbps) */
2940 G_TYPE_STRING, /* Status */
2941 G_TYPE_STRING, /* Date */
2942 G_TYPE_UINT, /* Length */
2943 G_TYPE_STRING, /* Foreground color */
2944 G_TYPE_STRING); /* Background color */
2947 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2949 list_view = GTK_TREE_VIEW(list);
2950 sortable = GTK_TREE_SORTABLE(list_store);
2952 #if GTK_CHECK_VERSION(2,6,0)
2953 /* Speed up the list display */
2954 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2957 /* Setup the sortable columns */
2958 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2959 gtk_tree_view_set_headers_clickable(list_view, FALSE);
2961 /* The view now holds a reference. We can get rid of our own reference */
2962 g_object_unref (G_OBJECT (list_store));
2965 * Create the first column packet, associating the "text" attribute of the
2966 * cell_renderer to the first column of the model
2968 renderer = gtk_cell_renderer_text_new ();
2969 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
2970 "text", PACKET_COLUMN,
2971 "foreground", FOREGROUND_COLOR_COL,
2972 "background", BACKGROUND_COLOR_COL,
2974 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2975 gtk_tree_view_column_set_resizable(column, TRUE);
2976 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2977 gtk_tree_view_column_set_min_width(column, 100);
2979 /* Add the column to the view. */
2980 gtk_tree_view_append_column (list_view, column);
2982 /* Second column.. Delta(ms). */
2983 renderer = gtk_cell_renderer_text_new ();
2984 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
2985 "text", DELTA_COLUMN,
2986 "foreground", FOREGROUND_COLOR_COL,
2987 "background", BACKGROUND_COLOR_COL,
2990 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
2991 GINT_TO_POINTER(DELTA_COLUMN), NULL);
2993 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
2994 gtk_tree_view_column_set_resizable(column, TRUE);
2995 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2996 gtk_tree_view_column_set_min_width(column, 100);
2997 gtk_tree_view_append_column (list_view, column);
2999 /* Third column.. Jitter(ms). */
3000 renderer = gtk_cell_renderer_text_new ();
3001 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3002 "text", JITTER_COLUMN,
3003 "foreground", FOREGROUND_COLOR_COL,
3004 "background", BACKGROUND_COLOR_COL,
3007 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3008 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3010 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3011 gtk_tree_view_column_set_resizable(column, TRUE);
3012 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3013 gtk_tree_view_column_set_min_width(column, 100);
3014 gtk_tree_view_append_column (list_view, column);
3016 /* Fourth column.. IP BW(kbps). */
3017 renderer = gtk_cell_renderer_text_new ();
3018 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3019 "text", IPBW_COLUMN,
3020 "foreground", FOREGROUND_COLOR_COL,
3021 "background", BACKGROUND_COLOR_COL,
3024 gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
3025 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3027 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3028 gtk_tree_view_column_set_resizable(column, TRUE);
3029 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3030 gtk_tree_view_column_set_min_width(column, 100);
3031 gtk_tree_view_append_column (list_view, column);
3033 /* Fifth column.. Status. */
3034 renderer = gtk_cell_renderer_text_new ();
3035 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3036 "text", STATUS_COLUMN,
3037 "foreground", FOREGROUND_COLOR_COL,
3038 "background", BACKGROUND_COLOR_COL,
3040 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3041 gtk_tree_view_column_set_resizable(column, TRUE);
3042 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3043 gtk_tree_view_column_set_min_width(column, 100);
3044 gtk_tree_view_append_column (list_view, column);
3046 /* Sixth column.. Length. */
3047 renderer = gtk_cell_renderer_text_new ();
3048 column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
3049 "text", LENGTH_COLUMN,
3050 "foreground", FOREGROUND_COLOR_COL,
3051 "background", BACKGROUND_COLOR_COL,
3055 gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
3056 gtk_tree_view_column_set_resizable(column, TRUE);
3057 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3058 gtk_tree_view_column_set_min_width(column, 100);
3059 gtk_tree_view_append_column (list_view, column);
3061 /* Now enable the sorting of each column */
3062 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3063 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3065 /* Setup the selection handler */
3066 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3067 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3069 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3070 G_CALLBACK (on_list_select_row),
3077 /****************************************************************************/
3078 /* Create the dialog box with all widgets */
3079 static void create_iax2_dialog(user_data_t* user_data)
3081 GtkWidget *window = NULL;
3082 GtkWidget *list_fwd;
3083 GtkWidget *list_rev;
3084 GtkWidget *label_stats_fwd;
3085 GtkWidget *label_stats_rev;
3086 GtkWidget *notebook;
3088 GtkWidget *main_vb, *page, *page_r;
3090 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3091 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3092 #ifdef USE_CONVERSATION_GRAPH
3093 GtkWidget *graph_bt;
3095 GtkWidget *graph_bt;
3096 gchar label_forward[150];
3097 gchar label_reverse[150];
3099 gchar str_ip_src[16];
3100 gchar str_ip_dst[16];
3102 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: IAX2 Stream Analysis");
3103 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3105 /* Container for each row of widgets */
3106 main_vb = gtk_vbox_new(FALSE, 2);
3107 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3108 gtk_container_add(GTK_CONTAINER(window), main_vb);
3109 gtk_widget_show(main_vb);
3112 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3113 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3115 g_snprintf(label_forward, sizeof(label_forward),
3116 "Analysing stream from %s port %u to %s port %u ",
3117 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
3120 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3121 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3123 g_snprintf(label_reverse, sizeof(label_reverse),
3124 "Analysing stream from %s port %u to %s port %u ",
3125 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
3127 /* Start a notebook for flipping between sets of changes */
3128 notebook = gtk_notebook_new();
3129 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3130 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3132 user_data->dlg.notebook_signal_id =
3133 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3135 /* page for forward connection */
3136 page = gtk_vbox_new(FALSE, 8);
3137 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3139 /* direction label */
3140 label = gtk_label_new(label_forward);
3141 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3143 /* place for some statistics */
3144 label_stats_fwd = gtk_label_new("\n");
3145 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3147 /* scrolled window */
3148 scrolled_window = scrolled_window_new(NULL, NULL);
3151 list_fwd = create_list(user_data);
3152 gtk_widget_show(list_fwd);
3153 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3154 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3155 gtk_widget_show(scrolled_window);
3158 label = gtk_label_new(" Forward Direction ");
3159 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3161 /* same page for reversed connection */
3162 page_r = gtk_vbox_new(FALSE, 8);
3163 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3164 label = gtk_label_new(label_reverse);
3165 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3166 label_stats_rev = gtk_label_new("\n");
3167 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3169 scrolled_window_r = scrolled_window_new(NULL, NULL);
3171 list_rev = create_list(user_data);
3172 gtk_widget_show(list_rev);
3173 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3174 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3175 gtk_widget_show(scrolled_window_r);
3177 label = gtk_label_new(" Reversed Direction ");
3178 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3180 /* page for help&about or future
3181 page_help = gtk_hbox_new(FALSE, 5);
3182 label = gtk_label_new(" Future ");
3183 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3184 frame = gtk_frame_new("");
3185 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3186 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3187 gtk_container_add(GTK_CONTAINER(frame), text);
3188 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3189 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3192 /* show all notebooks */
3193 gtk_widget_show_all(notebook);
3196 box4 = gtk_hbutton_box_new();
3197 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3198 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3199 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3200 gtk_box_set_spacing(GTK_BOX (box4), 0);
3201 gtk_widget_show(box4);
3203 voice_bt = gtk_button_new_with_label("Save payload...");
3204 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3205 gtk_widget_show(voice_bt);
3206 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3208 csv_bt = gtk_button_new_with_label("Save as CSV...");
3209 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3210 gtk_widget_show(csv_bt);
3211 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3213 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3214 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3215 gtk_widget_show(refresh_bt);
3216 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3218 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3219 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3220 gtk_widget_show(goto_bt);
3221 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3223 graph_bt = gtk_button_new_with_label("Graph");
3224 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3225 gtk_widget_show(graph_bt);
3226 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3229 #ifdef USE_CONVERSATION_GRAPH
3230 graph_bt = gtk_button_new_with_label("Graph");
3231 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3232 gtk_widget_show(graph_bt);
3233 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3236 next_bt = gtk_button_new_with_label("Next non-Ok");
3237 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3238 gtk_widget_show(next_bt);
3239 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3241 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3242 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3243 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3244 gtk_widget_show(close_bt);
3245 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3247 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3248 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3250 gtk_widget_show(window);
3251 window_present(window);
3253 /* some widget references need to be saved for outside use */
3254 user_data->dlg.window = window;
3255 user_data->dlg.list_fwd = list_fwd;
3256 user_data->dlg.list_rev = list_rev;
3257 user_data->dlg.label_stats_fwd = label_stats_fwd;
3258 user_data->dlg.label_stats_rev = label_stats_rev;
3259 user_data->dlg.notebook = notebook;
3260 user_data->dlg.selected_list = list_fwd;
3261 user_data->dlg.number_of_nok = 0;
3264 * select the initial row
3266 gtk_widget_grab_focus(list_fwd);
3270 /****************************************************************************/
3271 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3272 const gchar* proto_field, guint32* p_result)
3275 proto_node *proto_sibling_node;
3276 header_field_info *hfssrc;
3279 finfo = PITEM_FINFO(ptree_node);
3281 if (hfinformation==(finfo->hfinfo)) {
3282 hfssrc = proto_registrar_get_byname(proto_field);
3283 if (hfssrc == NULL) {
3284 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3288 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3289 ptree_node=ptree_node->next) {
3290 finfo=PITEM_FINFO(ptree_node);
3291 if (hfssrc==finfo->hfinfo) {
3292 if (hfinformation->type==FT_IPv4) {
3293 ipv4 = fvalue_get(&finfo->value);
3294 *p_result = ipv4_get_net_order_addr(ipv4);
3297 *p_result = fvalue_get_uinteger(&finfo->value);
3306 proto_sibling_node = ptree_node->next;
3308 if (proto_sibling_node) {
3309 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3315 /****************************************************************************/
3316 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3317 const gchar* proto_name,
3318 const gchar* proto_field,
3321 proto_node *ptree_node;
3322 header_field_info *hfinformation;
3324 hfinformation = proto_registrar_get_byname(proto_name);
3325 if (hfinformation == NULL) {
3326 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3331 ptree_node = ((proto_node *)protocol_tree)->first_child;
3333 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3337 return process_node(ptree_node, hfinformation, proto_field, p_result);
3341 /****************************************************************************/
3343 address *ip_src_fwd,
3344 guint16 port_src_fwd,
3345 address *ip_dst_fwd,
3346 guint16 port_dst_fwd,
3347 address *ip_src_rev,
3348 guint16 port_src_rev,
3349 address *ip_dst_rev,
3350 guint16 port_dst_rev
3353 user_data_t *user_data;
3356 static color_t col[MAX_GRAPHS] = {
3357 {0, 0x0000, 0x0000, 0x0000},
3358 {0, 0xffff, 0x0000, 0x0000},
3359 {0, 0x0000, 0xffff, 0x0000},
3360 {0, 0x0000, 0x0000, 0xffff}
3364 user_data = g_malloc(sizeof(user_data_t));
3366 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3367 user_data->port_src_fwd = port_src_fwd;
3368 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3369 user_data->port_dst_fwd = port_dst_fwd;
3370 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3371 user_data->port_src_rev = port_src_rev;
3372 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3373 user_data->port_dst_rev = port_dst_rev;
3376 /* file names for storing sound data */
3377 /*XXX: check for errors*/
3378 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3381 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3384 user_data->forward.saveinfo.fp = NULL;
3385 user_data->reversed.saveinfo.fp = NULL;
3386 user_data->dlg.save_voice_as_w = NULL;
3387 user_data->dlg.save_csv_as_w = NULL;
3388 user_data->dlg.dialog_graph.window = NULL;
3390 #ifdef USE_CONVERSATION_GRAPH
3391 user_data->dlg.graph_window = NULL;
3392 user_data->series_fwd.value_pairs = NULL;
3393 user_data->series_rev.value_pairs = NULL;
3396 /* init dialog_graph */
3397 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3398 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3399 user_data->dlg.dialog_graph.draw_area=NULL;
3400 user_data->dlg.dialog_graph.pixmap=NULL;
3401 user_data->dlg.dialog_graph.scrollbar=NULL;
3402 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3403 user_data->dlg.dialog_graph.pixmap_width=500;
3404 user_data->dlg.dialog_graph.pixmap_height=200;
3405 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3406 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3407 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3408 user_data->dlg.dialog_graph.max_interval=0;
3409 user_data->dlg.dialog_graph.num_items=0;
3410 user_data->dlg.dialog_graph.start_time = -1;
3412 for(i=0;i<MAX_GRAPHS;i++){
3413 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3414 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3415 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3416 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3417 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3418 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3419 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3420 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3423 /* create the dialog box */
3424 create_iax2_dialog(user_data);
3426 /* proceed as if the Refresh button would have been pressed */
3427 on_refresh_bt_clicked(NULL, user_data);
3430 /****************************************************************************/
3431 /* entry point from main menu */
3432 static void iax2_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3435 guint16 port_src_fwd;
3437 guint16 port_dst_fwd;
3439 guint16 port_src_rev;
3441 guint16 port_dst_rev;
3442 /* unsigned int ptype; */
3444 gchar filter_text[256];
3447 epan_dissect_t *edt;
3450 gboolean frame_matched;
3452 GList *strinfo_list;
3453 GList *filtered_list = NULL;
3454 rtp_stream_info_t *strinfo;
3457 /* Try to compile the filter. */
3458 g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
3459 if (!dfilter_compile(filter_text, &sfcode)) {
3460 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3463 /* we load the current file into cf variable */
3465 fdata = cf->current_frame;
3467 /* we are on the selected frame now */
3469 return; /* if we exit here it's an error */
3471 /* dissect the current frame */
3472 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3473 cf->pd, fdata->cap_len, &err, &err_info)) {
3474 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3475 cf_read_error_message(err, err_info), cf->filename);
3478 edt = epan_dissect_new(TRUE, FALSE);
3479 epan_dissect_prime_dfilter(edt, sfcode);
3480 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3481 frame_matched = dfilter_apply_edt(sfcode, edt);
3483 /* if it is not an iax2 frame, show an error dialog */
3484 frame_matched = dfilter_apply_edt(sfcode, edt);
3485 if (frame_matched != 1) {
3486 epan_dissect_free(edt);
3487 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3488 "You didn't choose a IAX2 packet!");
3491 /* check if it is Voice or MiniPacket
3492 if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
3493 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3494 "Please select a Voice packet!");
3498 /* check if it is part of a Call */
3499 if (edt->pi.circuit_id == 0) {
3500 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3501 "Please select a Call packet!");
3505 /* ok, it is a IAX2 frame, so let's get the ip and port values */
3506 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3507 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3508 port_src_fwd = edt->pi.srcport;
3509 port_dst_fwd = edt->pi.destport;
3511 /* assume the inverse ip/port combination for the reverse direction */
3512 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3513 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3514 port_src_rev = edt->pi.destport;
3515 port_dst_rev = edt->pi.srcport;
3517 /* Scan for rtpstream */
3519 /* search for reversed direction in the global rtp streams list */
3521 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3522 while (strinfo_list)
3524 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3525 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3526 && strinfo->src_port==port_src_fwd
3527 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3528 && strinfo->dest_port==port_dst_fwd)
3530 filtered_list = g_list_prepend(filtered_list, strinfo);
3533 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3534 && strinfo->src_port==port_src_rev
3535 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3536 && strinfo->dest_port==port_dst_rev)
3539 filtered_list = g_list_append(filtered_list, strinfo);
3542 strinfo_list = g_list_next(strinfo_list);
3545 /* if more than one reverse streams found, we let the user choose the right one */
3547 rtpstream_dlg_show(filtered_list);
3564 /****************************************************************************/
3566 iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
3568 iax2_analysis_cb(NULL, NULL);
3571 /****************************************************************************/
3573 register_tap_listener_iax2_analysis(void)
3575 register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
3577 register_stat_menu_item("IAX2/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3578 iax2_analysis_cb, NULL, NULL, NULL);
3588 * indent-tabs-mode: t
3591 * ex: set shiftwidth=8 tabstop=8 noexpandtab
3592 * :indentSize=8:tabSize=8:noTabs=false: