2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 /*do not define this symbol. will be added soon*/
41 /*#define USE_CONVERSATION_GRAPH 1*/
43 #ifdef USE_CONVERSATION_GRAPH
44 #include "../graph/graph.h"
47 #include <epan/epan_dissect.h>
48 #include <epan/filesystem.h>
49 #include <epan/pint.h>
54 #include <epan/dissectors/packet-rtp.h>
56 #include <epan/rtp_pt.h>
57 #include <epan/addr_resolv.h>
61 #include "gtkglobals.h"
63 #include <epan/stat_cmd_args.h>
64 #include "dlg_utils.h"
66 #include "gui_utils.h"
67 #include "alert_box.h"
68 #include "simple_dialog.h"
69 #include "../stat_menu.h"
70 #include "gui_stat_menu.h"
72 #include "progress_dlg.h"
75 #include "image/clist_ascend.xpm"
76 #include "image/clist_descend.xpm"
81 #include <epan/strutil.h>
91 #include "file_util.h"
94 #include "rtp_analysis.h"
95 #include "rtp_stream.h"
96 #include "rtp_stream_dlg.h"
99 /****************************************************************************/
101 typedef struct column_arrows {
103 GtkWidget *ascend_pm;
104 GtkWidget *descend_pm;
108 #define NUM_GRAPH_ITEMS 100000
109 #define MAX_YSCALE 16
110 #define AUTO_MAX_YSCALE 0
112 #define GRAPH_FWD_JITTER 0
113 #define GRAPH_FWD_DIFF 1
114 #define GRAPH_REV_JITTER 2
115 #define GRAPH_REV_DIFF 3
116 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
118 #define MAX_PIXELS_PER_TICK 4
119 #define DEFAULT_PIXELS_PER_TICK 1
120 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
121 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
123 #define MAX_TICK_VALUES 5
124 #define DEFAULT_TICK_VALUE 1
125 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
126 typedef struct _dialog_graph_graph_item_t {
129 } dialog_graph_graph_item_t;
131 typedef struct _dialog_graph_graph_t {
132 struct _user_data_t *ud;
133 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
136 GtkWidget *display_button;
141 } dialog_graph_graph_t;
144 typedef struct _dialog_graph_t {
145 gboolean needs_redraw;
146 gint32 interval; /* measurement interval in ms */
147 guint32 last_interval;
148 guint32 max_interval; /* XXX max_interval and num_items are redundant */
150 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
152 GtkWidget *draw_area;
154 GtkAdjustment *scrollbar_adjustment;
155 GtkWidget *scrollbar;
163 typedef struct _dialog_data_t {
167 GtkWidget *label_stats_fwd;
168 GtkWidget *label_stats_rev;
169 column_arrows *col_arrows_fwd;
170 column_arrows *col_arrows_rev;
172 GtkCList *selected_clist;
173 GtkWidget *save_voice_as_w;
174 GtkWidget *save_csv_as_w;
175 gint notebook_signal_id;
177 dialog_graph_t dialog_graph;
178 #ifdef USE_CONVERSATION_GRAPH
179 GtkWidget *graph_window;
183 #define OK_TEXT "[ Ok ]"
185 /* type of error when saving voice in a file didn't succeed */
188 TAP_RTP_WRONG_LENGTH,
189 TAP_RTP_PADDING_ERROR,
191 TAP_RTP_FILE_OPEN_ERROR,
195 typedef struct _tap_rtp_save_info_t {
198 error_type_t error_type;
200 } tap_rtp_save_info_t;
203 /* structure that holds the information about the forward and reversed direction */
204 struct _info_direction {
205 tap_rtp_stat_t statinfo;
206 tap_rtp_save_info_t saveinfo;
209 #define TMPNAMSIZE 100
211 #define SILENCE_PCMU (guint8)0xFF
212 #define SILENCE_PCMA (guint8)0x55
214 /* structure that holds general information about the connection
215 * and structures for both directions */
216 typedef struct _user_data_t {
217 /* tap associated data*/
219 guint16 port_src_fwd;
221 guint16 port_dst_fwd;
224 guint16 port_src_rev;
226 guint16 port_dst_rev;
229 struct _info_direction forward;
230 struct _info_direction reversed;
232 char f_tempname[TMPNAMSIZE];
233 char r_tempname[TMPNAMSIZE];
235 /* dialog associated data */
238 #ifdef USE_CONVERSATION_GRAPH
239 time_series_t series_fwd;
240 time_series_t series_rev;
246 static const gchar *titles[9] = {
258 #define SAVE_FORWARD_DIRECTION_MASK 0x01
259 #define SAVE_REVERSE_DIRECTION_MASK 0x02
260 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
262 #define SAVE_NONE_FORMAT 0
263 #define SAVE_WAV_FORMAT 1
264 #define SAVE_AU_FORMAT 2
265 #define SAVE_SW_FORMAT 3
266 #define SAVE_RAW_FORMAT 4
269 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
270 /****************************************************************************/
271 static void enable_graph(dialog_graph_graph_t *dgg)
278 static void dialog_graph_reset(user_data_t* user_data);
282 /****************************************************************************/
285 /****************************************************************************/
286 /* when there is a [re]reading of packet's */
288 rtp_reset(void *user_data_arg)
290 user_data_t *user_data = user_data_arg;
291 user_data->forward.statinfo.first_packet = TRUE;
292 user_data->reversed.statinfo.first_packet = TRUE;
293 user_data->forward.statinfo.max_delta = 0;
294 user_data->reversed.statinfo.max_delta = 0;
295 user_data->forward.statinfo.max_jitter = 0;
296 user_data->reversed.statinfo.max_jitter = 0;
297 user_data->forward.statinfo.mean_jitter = 0;
298 user_data->reversed.statinfo.mean_jitter = 0;
299 user_data->forward.statinfo.delta = 0;
300 user_data->reversed.statinfo.delta = 0;
301 user_data->forward.statinfo.diff = 0;
302 user_data->reversed.statinfo.diff = 0;
303 user_data->forward.statinfo.jitter = 0;
304 user_data->reversed.statinfo.jitter = 0;
305 user_data->forward.statinfo.bandwidth = 0;
306 user_data->reversed.statinfo.bandwidth = 0;
307 user_data->forward.statinfo.total_bytes = 0;
308 user_data->reversed.statinfo.total_bytes = 0;
309 user_data->forward.statinfo.bw_start_index = 0;
310 user_data->reversed.statinfo.bw_start_index = 0;
311 user_data->forward.statinfo.bw_index = 0;
312 user_data->reversed.statinfo.bw_index = 0;
313 user_data->forward.statinfo.timestamp = 0;
314 user_data->reversed.statinfo.timestamp = 0;
315 user_data->forward.statinfo.max_nr = 0;
316 user_data->reversed.statinfo.max_nr = 0;
317 user_data->forward.statinfo.total_nr = 0;
318 user_data->reversed.statinfo.total_nr = 0;
319 user_data->forward.statinfo.sequence = 0;
320 user_data->reversed.statinfo.sequence = 0;
321 user_data->forward.statinfo.start_seq_nr = 0;
322 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
323 user_data->forward.statinfo.stop_seq_nr = 0;
324 user_data->reversed.statinfo.stop_seq_nr = 0;
325 user_data->forward.statinfo.cycles = 0;
326 user_data->reversed.statinfo.cycles = 0;
327 user_data->forward.statinfo.under = FALSE;
328 user_data->reversed.statinfo.under = FALSE;
329 user_data->forward.statinfo.start_time = 0;
330 user_data->reversed.statinfo.start_time = 0;
331 user_data->forward.statinfo.time = 0;
332 user_data->reversed.statinfo.time = 0;
333 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
334 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
336 user_data->forward.saveinfo.count = 0;
337 user_data->reversed.saveinfo.count = 0;
338 user_data->forward.saveinfo.saved = FALSE;
339 user_data->reversed.saveinfo.saved = FALSE;
341 /* clear the dialog box clists */
342 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
343 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
345 /* reset graph info */
346 dialog_graph_reset(user_data);
348 #ifdef USE_CONVERSATION_GRAPH
349 if (user_data->dlg.graph_window != NULL)
350 window_destroy(user_data->dlg.graph_window);
352 g_array_free(user_data->series_fwd.value_pairs, TRUE);
353 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
355 g_array_free(user_data->series_rev.value_pairs, TRUE);
356 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
359 /* XXX check for error at fclose? */
360 if (user_data->forward.saveinfo.fp != NULL)
361 fclose(user_data->forward.saveinfo.fp);
362 if (user_data->reversed.saveinfo.fp != NULL)
363 fclose(user_data->reversed.saveinfo.fp);
364 user_data->forward.saveinfo.fp = eth_fopen(user_data->f_tempname, "wb");
365 if (user_data->forward.saveinfo.fp == NULL)
366 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
367 user_data->reversed.saveinfo.fp = eth_fopen(user_data->r_tempname, "wb");
368 if (user_data->reversed.saveinfo.fp == NULL)
369 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
373 /****************************************************************************/
374 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
376 dialog_graph_graph_item_t *it;
380 /* we sometimes get called when dgg is disabled.
381 this is a bug since the tap listener should be removed first */
386 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
389 * Find which interval this is supposed to to in and store the
390 * interval index as idx
392 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
393 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
395 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
399 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
401 /* some sanity checks */
402 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
406 /* update num_items */
407 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
408 dgg->ud->dlg.dialog_graph.num_items=idx;
409 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
413 * Find the appropriate dialog_graph_graph_item_t structure
418 * Use the max value to highlight RTP problems
420 if (value > it->value) {
423 it->flags = it->flags | statinfo->flags;
428 /****************************************************************************/
429 /* here we can redraw the output */
431 static void rtp_draw(void *prs _U_)
436 /* forward declarations */
437 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
438 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
439 gchar *timeStr, guint32 pkt_len, GdkColor *color);
441 static int rtp_packet_add_info(GtkCList *clist,
442 tap_rtp_stat_t *statinfo, packet_info *pinfo,
443 const struct _rtp_info *rtpinfo);
445 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
446 tap_rtp_stat_t *statinfo,
448 const struct _rtp_info *rtpinfo);
451 /****************************************************************************/
452 /* whenever a RTP packet is seen by the tap listener */
453 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
455 user_data_t *user_data = user_data_arg;
456 const struct _rtp_info *rtpinfo = rtpinfo_arg;
457 #ifdef USE_CONVERSATION_GRAPH
460 /* we ignore packets that are not displayed */
461 if (pinfo->fd->flags.passed_dfilter == 0)
463 /* also ignore RTP Version != 2 */
464 else if (rtpinfo->info_version !=2)
466 /* is it the forward direction? */
467 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
468 && 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 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
478 rtp_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 rtp_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 rtp_packet_add_info(user_data->dlg.clist_fwd,
481 &(user_data->forward.statinfo), pinfo, rtpinfo);
482 rtp_packet_save_payload(&(user_data->forward.saveinfo),
483 &(user_data->forward.statinfo), pinfo, rtpinfo);
485 /* is it the reversed direction? */
486 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
487 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
488 && user_data->port_src_rev == pinfo->srcport
489 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
490 && user_data->port_dst_rev == pinfo->destport) {
491 #ifdef USE_CONVERSATION_GRAPH
492 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
493 vp.fnumber = pinfo->fd->num;
494 g_array_append_val(user_data->series_rev.value_pairs, vp);
496 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
497 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
498 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
499 rtp_packet_add_info(user_data->dlg.clist_rev,
500 &(user_data->reversed.statinfo), pinfo, rtpinfo);
501 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
502 &(user_data->reversed.statinfo), pinfo, rtpinfo);
509 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
510 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
511 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
512 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
514 /****************************************************************************/
515 /* adds statistics information from the packet to the clist */
516 static int rtp_packet_add_info(GtkCList *clist,
517 tap_rtp_stat_t *statinfo, packet_info *pinfo,
518 const struct _rtp_info *rtpinfo)
525 GdkColor color = COLOR_DEFAULT;
526 then = pinfo->fd->abs_ts.secs;
527 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
528 tm_tmp = localtime(&then);
529 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
532 tm_tmp->tm_year + 1900,
538 if (statinfo->pt == PT_CN) {
539 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
542 else if (statinfo->pt == PT_CN_OLD) {
543 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
546 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
547 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
550 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
551 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
552 color = COLOR_WARNING;
554 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
555 g_snprintf(status,sizeof(status),"Incorrect timestamp");
556 color = COLOR_WARNING;
558 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
559 && !(statinfo->flags & STAT_FLAG_FIRST)
560 && !(statinfo->flags & STAT_FLAG_PT_CN)
561 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
562 && !(statinfo->flags & STAT_FLAG_MARKER)) {
563 g_snprintf(status,sizeof(status),"Marker missing?");
564 color = COLOR_WARNING;
567 if (statinfo->flags & STAT_FLAG_MARKER) {
568 color = COLOR_WARNING;
570 g_snprintf(status,sizeof(status),OK_TEXT);
572 /* is this the first packet we got in this direction? */
573 if (statinfo->flags & STAT_FLAG_FIRST) {
575 pinfo->fd->num, rtpinfo->info_seq_num,
580 rtpinfo->info_marker_set,
581 timeStr, pinfo->fd->pkt_len,
586 pinfo->fd->num, rtpinfo->info_seq_num,
587 statinfo->delta*1000,
588 statinfo->jitter*1000,
591 rtpinfo->info_marker_set,
592 timeStr, pinfo->fd->pkt_len,
598 #define MAX_SILENCE_TICKS 1000000
599 /****************************************************************************/
600 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
601 tap_rtp_stat_t *statinfo,
603 const struct _rtp_info *rtpinfo)
610 /* is this the first packet we got in this direction? */
611 if (statinfo->flags & STAT_FLAG_FIRST) {
612 if (saveinfo->fp == NULL) {
613 saveinfo->saved = FALSE;
614 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
617 saveinfo->saved = TRUE;
620 /* save the voice information */
621 /* if there was already an error, we quit */
622 if (saveinfo->saved == FALSE)
625 /* if the captured length and packet length aren't equal, we quit
626 * if also the RTP dissector thinks there is some information missing */
627 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
628 (!rtpinfo->info_all_data_present)) {
629 saveinfo->saved = FALSE;
630 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
634 /* if padding bit is set, but the padding count is bigger
635 * then the whole RTP data - error with padding count */
636 if ( (rtpinfo->info_padding_set != FALSE) &&
637 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
638 saveinfo->saved = FALSE;
639 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
643 /* do we need to insert some silence? */
644 if ((rtpinfo->info_marker_set) &&
645 !(statinfo->flags & STAT_FLAG_FIRST) &&
646 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
647 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
648 /* the amount of silence should be the difference between
649 * the last timestamp and the current one minus x
650 * x should equal the amount of information in the last frame
651 * XXX not done yet */
652 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
653 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
654 switch (statinfo->reg_pt) {
665 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
668 fflush(saveinfo->fp);
672 if (rtpinfo->info_payload_type == PT_CN
673 || rtpinfo->info_payload_type == PT_CN_OLD) {
675 /*all other payloads*/
677 if (!rtpinfo->info_all_data_present) {
678 /* Not all the data was captured. */
679 saveinfo->saved = FALSE;
680 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
684 /* we put the pointer at the beginning of the RTP
685 * payload, that is, at the beginning of the RTP data
686 * plus the offset of the payload from the beginning
688 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
689 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
690 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
692 fflush(saveinfo->fp);
693 saveinfo->saved = TRUE;
701 /****************************************************************************/
704 /****************************************************************************/
705 /* XXX just copied from gtk/rpc_stat.c */
706 void protect_thread_critical_region(void);
707 void unprotect_thread_critical_region(void);
710 /****************************************************************************/
711 /* close the dialog window and remove the tap listener */
712 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
714 /* remove tap listener */
715 protect_thread_critical_region();
716 remove_tap_listener(user_data);
717 unprotect_thread_critical_region();
719 /* close and remove temporary files */
720 if (user_data->forward.saveinfo.fp != NULL)
721 fclose(user_data->forward.saveinfo.fp);
722 if (user_data->reversed.saveinfo.fp != NULL)
723 fclose(user_data->reversed.saveinfo.fp);
724 /*XXX: test for error **/
725 eth_remove(user_data->f_tempname);
726 eth_remove(user_data->r_tempname);
728 /* destroy save_voice_as window if open */
729 if (user_data->dlg.save_voice_as_w != NULL)
730 window_destroy(user_data->dlg.save_voice_as_w);
732 /* destroy graph window if open */
733 if (user_data->dlg.dialog_graph.window != NULL)
734 window_destroy(user_data->dlg.dialog_graph.window);
736 #ifdef USE_CONVERSATION_GRAPH
737 /* destroy graph window if open */
738 if (user_data->dlg.graph_window != NULL)
739 window_destroy(user_data->dlg.graph_window);
742 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
743 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
745 g_free(user_data->dlg.col_arrows_fwd);
746 g_free(user_data->dlg.col_arrows_rev);
751 /****************************************************************************/
752 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
753 GtkNotebookPage *page _U_,
755 user_data_t *user_data _U_)
757 user_data->dlg.selected_clist =
758 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
759 user_data->dlg.selected_row = 0;
762 /****************************************************************************/
763 static void on_clist_select_row(GtkCList *clist _U_,
767 user_data_t *user_data _U_)
769 user_data->dlg.selected_clist = clist;
770 user_data->dlg.selected_row = row;
774 #ifdef USE_CONVERSATION_GRAPH
775 /****************************************************************************/
776 /* when the graph window gets destroyed */
777 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
779 /* note that graph window has been destroyed */
780 user_data->dlg.graph_window = NULL;
783 /****************************************************************************/
784 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
787 GtkCList *clist = NULL;
788 if (vp.fnumber != 0) {
789 clist = GTK_CLIST(user_data->dlg.clist_fwd);
790 row = gtk_clist_find_row_from_data(clist,
791 GUINT_TO_POINTER(vp.fnumber));
793 clist = GTK_CLIST(user_data->dlg.clist_rev);
794 row = gtk_clist_find_row_from_data(clist,
795 GUINT_TO_POINTER(vp.fnumber));
798 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
799 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
800 gtk_clist_select_row(clist, row, 0);
801 gtk_clist_moveto(clist, row, 0, 0.5, 0);
807 /****************************************************************************/
808 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
814 if (user_data->dlg.graph_window != NULL) {
815 /* There's already a graph window; reactivate it. */
816 reactivate_window(user_data->dlg.graph_window);
819 list = g_list_append(list, &(user_data->series_fwd));
820 list = g_list_append(list, &(user_data->series_rev));
822 user_data->series_fwd.color.pixel = 0;
823 user_data->series_fwd.color.red = 0x80ff;
824 user_data->series_fwd.color.green = 0xe0ff;
825 user_data->series_fwd.color.blue = 0xffff;
826 user_data->series_fwd.yvalue = 0.5;
828 user_data->series_rev.color.pixel = 0;
829 user_data->series_rev.color.red = 0x60ff;
830 user_data->series_rev.color.green = 0xc0ff;
831 user_data->series_rev.color.blue = 0xffff;
832 user_data->series_rev.yvalue = -0.5;
834 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=0x%X)",
835 get_addr_name(&(user_data->ip_src_fwd)),
836 user_data->port_src_fwd,
837 get_addr_name(&(user_data->ip_dst_fwd)),
838 user_data->port_dst_fwd,
839 user_data->ssrc_fwd);
841 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=0x%X)",
842 get_addr_name(&(user_data->ip_src_rev)),
843 user_data->port_src_rev,
844 get_addr_name(&(user_data->ip_dst_rev)),
845 user_data->port_dst_rev,
846 user_data->ssrc_rev);
848 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
849 &graph_selection_callback, user_data);
850 g_signal_connect(user_data->dlg.graph_window, "destroy",
851 G_CALLBACK(on_destroy_graph), user_data);
853 #endif /*USE_CONVERSATION_GRAPH*/
855 /****************************************************************************/
856 static void dialog_graph_set_title(user_data_t* user_data)
859 if (!user_data->dlg.dialog_graph.window){
862 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
863 get_addr_name(&(user_data->ip_src_fwd)),
864 user_data->port_src_fwd,
865 get_addr_name(&(user_data->ip_dst_fwd)),
866 user_data->port_dst_fwd,
867 get_addr_name(&(user_data->ip_src_rev)),
868 user_data->port_src_rev,
869 get_addr_name(&(user_data->ip_dst_rev)),
870 user_data->port_dst_rev);
872 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
878 /****************************************************************************/
879 static void dialog_graph_reset(user_data_t* user_data)
883 user_data->dlg.dialog_graph.needs_redraw=TRUE;
884 for(i=0;i<MAX_GRAPHS;i++){
885 for(j=0;j<NUM_GRAPH_ITEMS;j++){
886 dialog_graph_graph_item_t *dggi;
887 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
892 user_data->dlg.dialog_graph.last_interval=0xffffffff;
893 user_data->dlg.dialog_graph.max_interval=0;
894 user_data->dlg.dialog_graph.num_items=0;
896 /* create the color titles near the filter buttons */
897 for(i=0;i<MAX_GRAPHS;i++){
900 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
902 get_addr_name(&(user_data->ip_src_fwd)),
903 user_data->port_src_fwd,
904 get_addr_name(&(user_data->ip_dst_fwd)),
905 user_data->port_dst_fwd,
906 user_data->ssrc_fwd);
909 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
911 get_addr_name(&(user_data->ip_src_rev)),
912 user_data->port_src_rev,
913 get_addr_name(&(user_data->ip_dst_rev)),
914 user_data->port_dst_rev,
915 user_data->ssrc_rev);
919 dialog_graph_set_title(user_data);
922 /****************************************************************************/
923 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
925 dialog_graph_graph_item_t *it;
932 /****************************************************************************/
933 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
936 g_snprintf(buf, buf_len, "%ds",t/1000000);
937 } else if(t>=1000000){
938 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
940 g_snprintf(buf, buf_len, "%dms",t/1000);
942 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
944 g_snprintf(buf, buf_len, "%dus",t);
948 /****************************************************************************/
949 static void dialog_graph_draw(user_data_t* user_data)
952 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
953 gint32 current_interval;
954 guint32 left_x_border;
955 guint32 right_x_border;
956 guint32 top_y_border;
957 guint32 bottom_y_border;
959 int label_width, label_height;
960 guint32 draw_width, draw_height;
961 char label_string[15];
964 guint32 num_time_intervals;
965 guint32 max_value; /* max value of seen data */
966 guint32 max_y; /* max value of the Y scale */
968 if(!user_data->dlg.dialog_graph.needs_redraw){
971 user_data->dlg.dialog_graph.needs_redraw=FALSE;
974 * Find the length of the intervals we have data for
975 * so we know how large arrays we need to malloc()
977 num_time_intervals=user_data->dlg.dialog_graph.num_items;
978 /* if there isnt anything to do, just return */
979 if(num_time_intervals==0){
982 num_time_intervals+=1;
983 /* XXX move this check to _packet() */
984 if(num_time_intervals>NUM_GRAPH_ITEMS){
985 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
990 * find the max value so we can autoscale the y axis
993 for(i=0;i<MAX_GRAPHS;i++){
996 if(!user_data->dlg.dialog_graph.graph[i].display){
999 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1002 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1004 /* keep track of the max value we have encountered */
1012 * Clear out old plot
1014 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1015 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1018 user_data->dlg.dialog_graph.draw_area->allocation.width,
1019 user_data->dlg.dialog_graph.draw_area->allocation.height);
1023 * Calculate the y scale we should use
1025 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1026 max_y=yscale_max[MAX_YSCALE-1];
1027 for(i=MAX_YSCALE-1;i>0;i--){
1028 if(max_value<yscale_max[i]){
1029 max_y=yscale_max[i];
1033 /* the user had specified an explicit y scale to use */
1034 max_y=user_data->dlg.dialog_graph.max_y_units;
1038 * Calculate size of borders surrounding the plot
1039 * The border on the right side needs to be adjusted depending
1040 * on the width of the text labels. For simplicity we assume that the
1041 * top y scale label will be the widest one
1043 print_time_scale_string(label_string, 15, max_y);
1044 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1045 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1047 right_x_border=label_width+20;
1049 bottom_y_border=label_height+20;
1053 * Calculate the size of the drawing area for the actual plot
1055 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1056 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1060 * Draw the y axis and labels
1061 * (we always draw the y scale with 11 ticks along the axis)
1063 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1064 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1066 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1067 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1073 /* first, middle and last tick are slightly longer */
1077 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1078 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1079 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1080 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1081 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1082 /* draw the labels */
1084 print_time_scale_string(label_string, 15, (max_y*i/10));
1085 pango_layout_set_text(layout, label_string, -1);
1086 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1087 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1088 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1089 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1090 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1094 print_time_scale_string(label_string, 15, (max_y*i/10));
1095 pango_layout_set_text(layout, label_string, -1);
1096 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1097 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1098 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1099 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1100 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1104 print_time_scale_string(label_string, 15, (max_y*i/10));
1105 pango_layout_set_text(layout, label_string, -1);
1106 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1107 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1108 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1109 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1110 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1118 * if we have not specified the last_interval via the gui,
1119 * then just pick the current end of the capture so that is scrolls
1120 * nicely when doing live captures
1122 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1123 last_interval=user_data->dlg.dialog_graph.max_interval;
1125 last_interval=user_data->dlg.dialog_graph.last_interval;
1132 /* plot the x-scale */
1133 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);
1135 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1136 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1137 first_interval*=user_data->dlg.dialog_graph.interval;
1144 while(interval_delta<((last_interval-first_interval)/10)){
1145 interval_delta*=delta_multiplier;
1146 if(delta_multiplier==5){
1153 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1156 /* if pixels_per_tick is <5, only draw every 10 ticks */
1157 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1161 if(current_interval%interval_delta){
1167 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1168 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1169 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1170 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1171 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1172 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1176 if(user_data->dlg.dialog_graph.interval>=1000){
1177 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1178 } else if(user_data->dlg.dialog_graph.interval>=100){
1179 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1181 } else if(user_data->dlg.dialog_graph.interval>=10){
1182 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1185 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1187 pango_layout_set_text(layout, label_string, -1);
1188 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1189 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1190 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1191 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1192 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1204 * Draw "x" for Sequence Errors and "m" for Marks
1206 /* Draw the labels Fwd and Rev */
1207 g_strlcpy(label_string,"<-Fwd",15);
1208 pango_layout_set_text(layout, label_string, -1);
1209 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1210 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1211 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1212 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1213 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1215 g_strlcpy(label_string,"<-Rev",15);
1216 pango_layout_set_text(layout, label_string, -1);
1217 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1218 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1219 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1220 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1221 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1224 /* Draw the marks */
1225 for(i=MAX_GRAPHS-1;i>=0;i--){
1227 guint32 x_pos, prev_x_pos;
1229 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1230 if (!user_data->dlg.dialog_graph.graph[i].display){
1233 /* initialize prev x/y to the low left corner of the graph */
1234 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;
1236 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1237 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;
1239 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1241 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1242 g_strlcpy(label_string,"x",15);
1244 g_strlcpy(label_string,"m",15);
1247 pango_layout_set_text(layout, label_string, -1);
1248 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1249 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1250 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1252 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1260 g_object_unref(G_OBJECT(layout));
1263 * Loop over all graphs and draw them
1265 for(i=MAX_GRAPHS-1;i>=0;i--){
1267 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1268 if (!user_data->dlg.dialog_graph.graph[i].display){
1271 /* initialize prev x/y to the low left corner of the graph */
1272 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;
1273 prev_y_pos=draw_height-1+top_y_border;
1275 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1277 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;
1278 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1282 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1285 /* dont need to draw anything if the segment
1286 * is entirely above the top of the graph
1288 if( (prev_y_pos==0) && (y_pos==0) ){
1295 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1296 x_pos, draw_height-1+top_y_border,
1306 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1307 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1308 user_data->dlg.dialog_graph.pixmap,
1311 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1314 /* update the scrollbar */
1315 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1316 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1317 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1318 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1319 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1321 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1323 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1324 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1325 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1329 /****************************************************************************/
1330 static void dialog_graph_redraw(user_data_t* user_data)
1332 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1333 dialog_graph_draw(user_data);
1336 /****************************************************************************/
1337 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1339 user_data_t *user_data;
1341 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1343 user_data->dlg.dialog_graph.window = NULL;
1347 /****************************************************************************/
1348 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1350 user_data_t *user_data;
1352 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1358 gdk_draw_pixmap(widget->window,
1359 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1360 user_data->dlg.dialog_graph.pixmap,
1361 event->area.x, event->area.y,
1362 event->area.x, event->area.y,
1363 event->area.width, event->area.height);
1368 /****************************************************************************/
1369 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1371 user_data_t *user_data;
1374 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1380 if(user_data->dlg.dialog_graph.pixmap){
1381 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1382 user_data->dlg.dialog_graph.pixmap=NULL;
1385 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1386 widget->allocation.width,
1387 widget->allocation.height,
1389 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1390 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1392 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1393 widget->style->white_gc,
1396 widget->allocation.width,
1397 widget->allocation.height);
1399 /* set up the colors and the GC structs for this pixmap */
1400 for(i=0;i<MAX_GRAPHS;i++){
1401 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1402 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1405 dialog_graph_redraw(user_data);
1409 /****************************************************************************/
1410 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1412 user_data_t *user_data=(user_data_t *)data;
1415 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1416 if(user_data->dlg.dialog_graph.last_interval==mi){
1419 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1420 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1424 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1426 dialog_graph_redraw(user_data);
1430 /****************************************************************************/
1431 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1433 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1434 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1435 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1437 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);
1439 /* signals needed to handle backing pixmap */
1440 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1441 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1443 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1444 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1446 /* create the associated scrollbar */
1447 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1448 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1449 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1450 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1451 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1454 /****************************************************************************/
1455 static void disable_graph(dialog_graph_graph_t *dgg)
1459 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1464 /****************************************************************************/
1465 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1467 /* this graph is not active, just update display and redraw */
1468 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1470 dialog_graph_redraw(dgg->ud);
1475 cf_retap_packets(&cfile, FALSE);
1476 dialog_graph_redraw(dgg->ud);
1481 /****************************************************************************/
1482 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1488 hbox=gtk_hbox_new(FALSE, 3);
1489 gtk_container_add(GTK_CONTAINER(box), hbox);
1490 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1491 gtk_widget_show(hbox);
1493 g_snprintf(str, 256, "Graph %d", num);
1494 dgg->display_button=gtk_toggle_button_new_with_label(str);
1495 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1496 gtk_widget_show(dgg->display_button);
1497 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1498 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1500 label=gtk_label_new(dgg->title);
1501 gtk_widget_show(label);
1502 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1504 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1505 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1506 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1507 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1508 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1513 /****************************************************************************/
1514 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1521 frame=gtk_frame_new("Graphs");
1522 gtk_container_add(GTK_CONTAINER(box), frame);
1523 gtk_widget_show(frame);
1525 vbox=gtk_vbox_new(FALSE, 1);
1526 gtk_container_add(GTK_CONTAINER(frame), vbox);
1527 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1528 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1529 gtk_widget_show(vbox);
1531 for(i=0;i<MAX_GRAPHS;i++){
1532 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1535 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1536 gtk_widget_show(label);
1537 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1542 /****************************************************************************/
1543 static void yscale_select(GtkWidget *item, gpointer key)
1546 user_data_t *user_data;
1548 user_data=(user_data_t *)key;
1549 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1551 user_data->dlg.dialog_graph.max_y_units=val;
1552 dialog_graph_redraw(user_data);
1555 /****************************************************************************/
1556 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1559 user_data_t *user_data;
1561 user_data=(user_data_t *)key;
1562 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1563 user_data->dlg.dialog_graph.pixels_per_tick=val;
1564 dialog_graph_redraw(user_data);
1567 /****************************************************************************/
1568 static void tick_interval_select(GtkWidget *item, gpointer key)
1571 user_data_t *user_data;
1573 user_data=(user_data_t *)key;
1574 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1576 user_data->dlg.dialog_graph.interval=val;
1577 cf_retap_packets(&cfile, FALSE);
1578 dialog_graph_redraw(user_data);
1581 /****************************************************************************/
1582 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1585 GtkWidget *menu_item;
1588 for(i=0;i<MAX_YSCALE;i++){
1589 if(yscale_max[i]==AUTO_MAX_YSCALE){
1590 g_strlcpy(str,"Auto",15);
1592 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1594 menu_item=gtk_menu_item_new_with_label(str);
1595 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1596 GUINT_TO_POINTER(yscale_max[i]));
1597 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1598 gtk_widget_show(menu_item);
1599 gtk_menu_append(GTK_MENU(menu), menu_item);
1604 /****************************************************************************/
1605 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1608 GtkWidget *menu_item;
1611 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1612 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1613 menu_item=gtk_menu_item_new_with_label(str);
1615 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1616 GUINT_TO_POINTER(pixels_per_tick[i]));
1617 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1618 gtk_widget_show(menu_item);
1619 gtk_menu_append(GTK_MENU(menu), menu_item);
1621 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1626 /****************************************************************************/
1627 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1630 GtkWidget *menu_item;
1633 for(i=0;i<MAX_TICK_VALUES;i++){
1634 if(tick_interval_values[i]>=1000){
1635 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1636 } else if(tick_interval_values[i]>=100){
1637 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1638 } else if(tick_interval_values[i]>=10){
1639 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1641 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1644 menu_item=gtk_menu_item_new_with_label(str);
1645 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1646 GUINT_TO_POINTER(tick_interval_values[i]));
1647 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1648 gtk_widget_show(menu_item);
1649 gtk_menu_append(GTK_MENU(menu), menu_item);
1651 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1655 /****************************************************************************/
1656 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1660 GtkWidget *option_menu;
1663 hbox=gtk_hbox_new(FALSE, 0);
1664 gtk_container_add(GTK_CONTAINER(box), hbox);
1665 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1666 gtk_widget_show(hbox);
1668 label=gtk_label_new(name);
1669 gtk_widget_show(label);
1670 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1672 option_menu=gtk_option_menu_new();
1673 menu=gtk_menu_new();
1674 (*func)(user_data, menu);
1675 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1676 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1677 gtk_widget_show(option_menu);
1680 /****************************************************************************/
1681 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1683 GtkWidget *frame_vbox;
1687 frame_vbox=gtk_vbox_new(FALSE, 0);
1688 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1689 gtk_widget_show(frame_vbox);
1691 frame = gtk_frame_new("X Axis");
1692 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1693 gtk_widget_show(frame);
1695 vbox=gtk_vbox_new(FALSE, 0);
1696 gtk_container_add(GTK_CONTAINER(frame), vbox);
1697 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1698 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1699 gtk_widget_show(vbox);
1701 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1702 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1704 frame = gtk_frame_new("Y Axis");
1705 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1706 gtk_widget_show(frame);
1708 vbox=gtk_vbox_new(FALSE, 0);
1709 gtk_container_add(GTK_CONTAINER(frame), vbox);
1710 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1711 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1712 gtk_widget_show(vbox);
1714 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1719 /****************************************************************************/
1720 static void dialog_graph_init_window(user_data_t* user_data)
1724 GtkWidget *bt_close;
1726 /* create the main window */
1727 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1729 vbox=gtk_vbox_new(FALSE, 0);
1730 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1731 gtk_widget_show(vbox);
1733 create_draw_area(user_data, vbox);
1735 hbox=gtk_hbox_new(FALSE, 3);
1736 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1737 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
1738 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1739 gtk_widget_show(hbox);
1741 create_filter_area(user_data, hbox);
1742 create_ctrl_area(user_data, hbox);
1744 dialog_graph_set_title(user_data);
1746 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1747 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1748 gtk_widget_show(hbox);
1750 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1751 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1753 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1755 gtk_widget_show(user_data->dlg.dialog_graph.window);
1756 window_present(user_data->dlg.dialog_graph.window);
1761 /****************************************************************************/
1762 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1764 if (user_data->dlg.dialog_graph.window != NULL) {
1765 /* There's already a graph window; reactivate it. */
1766 reactivate_window(user_data->dlg.dialog_graph.window);
1770 dialog_graph_init_window(user_data);
1774 /****************************************************************************/
1775 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1779 if (user_data->dlg.selected_clist!=NULL) {
1780 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
1781 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
1782 cf_goto_frame(&cfile, fnumber);
1787 static void draw_stat(user_data_t *user_data);
1789 /****************************************************************************/
1790 /* re-dissects all packets */
1791 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1793 GString *error_string;
1795 /* remove tap listener */
1796 protect_thread_critical_region();
1797 remove_tap_listener(user_data);
1798 unprotect_thread_critical_region();
1800 /* register tap listener */
1801 error_string = register_tap_listener("rtp", user_data, NULL,
1802 rtp_reset, rtp_packet, rtp_draw);
1803 if (error_string != NULL) {
1804 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
1805 g_string_free(error_string, TRUE);
1809 /* retap all packets */
1810 cf_retap_packets(&cfile, FALSE);
1812 /* draw statistics info */
1813 draw_stat(user_data);
1815 gtk_clist_sort(user_data->dlg.clist_fwd);
1816 gtk_clist_sort(user_data->dlg.clist_rev);
1819 /****************************************************************************/
1820 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1825 if (user_data->dlg.selected_clist==NULL)
1828 clist = user_data->dlg.selected_clist;
1829 row = user_data->dlg.selected_row + 1;
1831 while (gtk_clist_get_text(clist,row,6,&text)) {
1832 if (strcmp(text, OK_TEXT) != 0) {
1833 gtk_clist_select_row(clist, row, 0);
1834 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1842 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
1843 if (strcmp(text, OK_TEXT) != 0) {
1844 gtk_clist_select_row(clist, row, 0);
1845 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1852 /****************************************************************************/
1853 /* when we want to save the information */
1854 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1857 GtkWidget *rev, *forw, *both;
1858 user_data_t *user_data;
1864 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1866 /* Perhaps the user specified a directory instead of a file.
1867 Check whether they did. */
1868 if (test_for_directory(g_dest) == EISDIR) {
1869 /* It's a directory - set the file selection box to display it. */
1870 set_last_open_dir(g_dest);
1872 file_selection_set_current_folder(fs, get_last_open_dir());
1876 rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1877 forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1878 both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1879 user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1881 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1882 fp = eth_fopen(g_dest, "w");
1884 open_failure_alert_box(g_dest, errno, TRUE);
1888 if (GTK_TOGGLE_BUTTON(both)->active) {
1889 fprintf(fp, "Forward\n");
1891 write_failure_alert_box(g_dest, errno);
1897 for(j = 0; j < NUM_COLS; j++) {
1899 fprintf(fp,"%s",titles[j]);
1901 fprintf(fp,",%s",titles[j]);
1906 write_failure_alert_box(g_dest, errno);
1910 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
1911 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1912 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1914 fprintf(fp,"%s",columnText);
1916 fprintf(fp,",%s",columnText);
1921 write_failure_alert_box(g_dest, errno);
1927 if (fclose(fp) == EOF) {
1928 write_failure_alert_box(g_dest, errno);
1933 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1935 if (GTK_TOGGLE_BUTTON(both)->active) {
1936 fp = eth_fopen(g_dest, "a");
1938 open_failure_alert_box(g_dest, errno, TRUE);
1941 fprintf(fp, "\nReverse\n");
1943 write_failure_alert_box(g_dest, errno);
1948 fp = eth_fopen(g_dest, "w");
1950 open_failure_alert_box(g_dest, errno, TRUE);
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 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1968 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1969 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1971 fprintf(fp,"%s",columnText);
1973 fprintf(fp,",%s",columnText);
1978 write_failure_alert_box(g_dest, errno);
1983 if (fclose(fp) == EOF) {
1984 write_failure_alert_box(g_dest, errno);
1989 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1992 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1994 user_data->dlg.save_csv_as_w = NULL;
1997 /* when the user wants to save the csv information in a file */
1998 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2002 GtkWidget *label_format;
2003 GtkWidget *channels_label;
2004 GSList *channels_group = NULL;
2005 GtkWidget *forward_rb;
2006 GtkWidget *reversed_rb;
2010 if (user_data->dlg.save_csv_as_w != NULL) {
2011 /* There's already a Save CSV info dialog box; reactivate it. */
2012 reactivate_window(user_data->dlg.save_csv_as_w);
2016 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Wireshark: Save Data As CSV");
2018 /* Container for each row of widgets */
2019 vertb = gtk_vbox_new(FALSE, 0);
2020 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2021 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2022 vertb, FALSE, FALSE, 0);
2023 gtk_widget_show (vertb);
2025 table1 = gtk_table_new (2, 4, FALSE);
2026 gtk_widget_show (table1);
2027 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2028 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2029 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2031 label_format = gtk_label_new ("Format: Comma Separated Values");
2032 gtk_widget_show (label_format);
2033 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2034 (GtkAttachOptions) (GTK_FILL),
2035 (GtkAttachOptions) (0), 0, 0);
2038 channels_label = gtk_label_new ("Channels:");
2039 gtk_widget_show (channels_label);
2040 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2041 (GtkAttachOptions) (GTK_FILL),
2042 (GtkAttachOptions) (0), 0, 0);
2043 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2045 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2046 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2047 gtk_widget_show (forward_rb);
2048 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2049 (GtkAttachOptions) (GTK_FILL),
2050 (GtkAttachOptions) (0), 0, 0);
2052 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2053 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2054 gtk_widget_show (reversed_rb);
2055 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2056 (GtkAttachOptions) (GTK_FILL),
2057 (GtkAttachOptions) (0), 0, 0);
2059 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2060 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2061 gtk_widget_show (both_rb);
2062 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2063 (GtkAttachOptions) (GTK_FILL),
2064 (GtkAttachOptions) (0), 0, 0);
2066 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2068 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2069 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2070 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2071 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2072 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2073 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_csv_as_ok_cb),
2074 user_data->dlg.save_csv_as_w);
2076 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2077 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, window_cancel_button_cb);
2079 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2080 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2081 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2083 gtk_widget_show(user_data->dlg.save_csv_as_w);
2084 window_present(user_data->dlg.save_csv_as_w);
2088 /****************************************************************************/
2089 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2091 /* Note that we no longer have a Save voice info dialog box. */
2092 user_data->dlg.save_voice_as_w = NULL;
2095 /****************************************************************************/
2096 /* here we save it into a file that user specified */
2097 /* XXX what about endians here? could go something wrong? */
2098 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2100 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2101 gchar f_pd[1] = {0};
2102 gchar r_pd[1] = {0};
2105 guint32 f_write_silence = 0;
2106 guint32 r_write_silence = 0;
2108 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2109 gboolean stop_flag = FALSE;
2112 forw_fd = eth_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2115 rev_fd = eth_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2121 /* open file for saving */
2122 to_fd = eth_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2129 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2131 if (format == SAVE_AU_FORMAT) /* au format */
2133 /* First we write the .au header. XXX Hope this is endian independant */
2134 /* the magic word 0x2e736e64 == .snd */
2135 phtonl(pd, 0x2e736e64);
2136 nchars=eth_write(to_fd, pd, 4);
2137 /* header offset == 24 bytes */
2139 nchars=eth_write(to_fd, pd, 4);
2140 /* total length, it is permited to set this to 0xffffffff */
2142 nchars=eth_write(to_fd, pd, 4);
2143 /* encoding format == 16-bit linear PCM */
2145 nchars=eth_write(to_fd, pd, 4);
2146 /* sample rate == 8000 Hz */
2148 nchars=eth_write(to_fd, pd, 4);
2151 nchars=eth_write(to_fd, pd, 4);
2155 /* only forward direction */
2156 case SAVE_FORWARD_DIRECTION_MASK: {
2157 progbar_count = user_data->forward.saveinfo.count;
2158 progbar_quantum = user_data->forward.saveinfo.count/100;
2159 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2162 if((count > progbar_nextstep) && (count <= progbar_count)) {
2163 update_progress_dlg(progbar,
2164 (gfloat) count/progbar_count, "Saving");
2165 progbar_nextstep = progbar_nextstep + progbar_quantum;
2169 if (user_data->forward.statinfo.pt == PT_PCMU){
2170 sample = ulaw2linear(*f_pd);
2173 else if(user_data->forward.statinfo.pt == PT_PCMA){
2174 sample = alaw2linear(*f_pd);
2181 destroy_progress_dlg(progbar);
2185 fwritten = eth_write(to_fd, pd, 2);
2186 if ((fwritten < 2) || (fwritten < 0) || (fread < 0)) {
2190 destroy_progress_dlg(progbar);
2196 /* only reversed direction */
2197 case SAVE_REVERSE_DIRECTION_MASK: {
2198 progbar_count = user_data->reversed.saveinfo.count;
2199 progbar_quantum = user_data->reversed.saveinfo.count/100;
2200 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2203 if((count > progbar_nextstep) && (count <= progbar_count)) {
2204 update_progress_dlg(progbar,
2205 (gfloat) count/progbar_count, "Saving");
2206 progbar_nextstep = progbar_nextstep + progbar_quantum;
2210 if (user_data->forward.statinfo.pt == PT_PCMU){
2211 sample = ulaw2linear(*f_pd);
2214 else if(user_data->forward.statinfo.pt == PT_PCMA){
2215 sample = alaw2linear(*f_pd);
2222 destroy_progress_dlg(progbar);
2226 rwritten = eth_write(to_fd, pd, 2);
2227 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2231 destroy_progress_dlg(progbar);
2237 /* both directions */
2238 case SAVE_BOTH_DIRECTION_MASK: {
2239 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2240 (progbar_count = user_data->forward.saveinfo.count) :
2241 (progbar_count = user_data->reversed.saveinfo.count);
2242 progbar_quantum = progbar_count/100;
2243 /* since conversation in one way can start later than in the other one,
2244 * we have to write some silence information for one channel */
2245 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2246 f_write_silence = (guint32)
2247 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2249 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2250 r_write_silence = (guint32)
2251 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2256 if((count > progbar_nextstep) && (count <= progbar_count)) {
2257 update_progress_dlg(progbar,
2258 (gfloat) count/progbar_count, "Saving");
2259 progbar_nextstep = progbar_nextstep + progbar_quantum;
2262 if(f_write_silence > 0) {
2263 rread = read(rev_fd, r_pd, 1);
2264 switch (user_data->forward.statinfo.reg_pt) {
2266 *f_pd = SILENCE_PCMU;
2269 *f_pd = SILENCE_PCMA;
2275 else if(r_write_silence > 0) {
2276 fread = read(forw_fd, f_pd, 1);
2277 switch (user_data->forward.statinfo.reg_pt) {
2279 *r_pd = SILENCE_PCMU;
2282 *r_pd = SILENCE_PCMA;
2289 fread = read(forw_fd, f_pd, 1);
2290 rread = read(rev_fd, r_pd, 1);
2292 if ((rread == 0) && (fread == 0))
2294 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2295 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2298 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2299 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2307 destroy_progress_dlg(progbar);
2312 rwritten = eth_write(to_fd, pd, 2);
2313 if ((rwritten < 2) || (rread < 0) || (fread < 0)) {
2317 destroy_progress_dlg(progbar);
2324 else if (format == SAVE_RAW_FORMAT) /* raw format */
2328 /* only forward direction */
2329 case SAVE_FORWARD_DIRECTION_MASK: {
2330 progbar_count = user_data->forward.saveinfo.count;
2331 progbar_quantum = user_data->forward.saveinfo.count/100;
2335 /* only reversed direction */
2336 case SAVE_REVERSE_DIRECTION_MASK: {
2337 progbar_count = user_data->reversed.saveinfo.count;
2338 progbar_quantum = user_data->reversed.saveinfo.count/100;
2346 destroy_progress_dlg(progbar);
2353 /* XXX how do you just copy the file? */
2354 while ((rread = read(fd, pd, 1)) > 0) {
2357 if((count > progbar_nextstep) && (count <= progbar_count)) {
2358 update_progress_dlg(progbar,
2359 (gfloat) count/progbar_count, "Saving");
2360 progbar_nextstep = progbar_nextstep + progbar_quantum;
2364 rwritten = eth_write(to_fd, pd, 1);
2366 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2370 destroy_progress_dlg(progbar);
2376 destroy_progress_dlg(progbar);
2384 /****************************************************************************/
2385 /* the user wants to save in a file */
2386 /* XXX support for different formats is currently commented out */
2387 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2390 /*GtkWidget *wav, *sw;*/
2391 GtkWidget *au, *raw;
2392 GtkWidget *rev, *forw, *both;
2393 user_data_t *user_data;
2394 gint channels , format;
2396 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2398 /* Perhaps the user specified a directory instead of a file.
2399 Check whether they did. */
2400 if (test_for_directory(g_dest) == EISDIR) {
2401 /* It's a directory - set the file selection box to display it. */
2402 set_last_open_dir(g_dest);
2404 file_selection_set_current_folder(fs, get_last_open_dir());
2408 /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2409 sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2410 au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2411 raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2412 rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2413 forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2414 both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2415 user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2417 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2418 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2419 * disable the ok button or disable the buttons for direction if only one is not ok. The
2420 * problem is if we open the save voice dialog and then click the refresh button and maybe
2421 * the state changes, so we can't save anymore. In this case we should be able to update
2422 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2425 /* we can not save in both directions */
2426 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2427 /* there are many combinations here, we just exit when first matches */
2428 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2429 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2430 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2431 "Can't save in a file: Unsupported codec!");
2432 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2433 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2434 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2435 "Can't save in a file: Wrong length of captured packets!");
2436 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2437 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2438 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2439 "Can't save in a file: RTP data with padding!");
2440 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2441 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2442 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2443 "Can't save in a file: Not all data in all packets was captured!");
2445 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2446 "Can't save in a file: File I/O problem!");
2449 /* we can not save forward direction */
2450 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2451 (GTK_TOGGLE_BUTTON (both)->active))) {
2452 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2453 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2454 "Can't save forward direction in a file: Unsupported codec!");
2455 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2456 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2457 "Can't save forward direction in a file: Wrong length of captured packets!");
2458 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2459 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2460 "Can't save forward direction in a file: RTP data with padding!");
2461 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2462 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2463 "Can't save forward direction in a file: Not all data in all packets was captured!");
2465 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2466 "Can't save forward direction in a file: File I/O problem!");
2469 /* we can not save reversed direction */
2470 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2471 (GTK_TOGGLE_BUTTON (both)->active))) {
2472 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2473 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2474 "Can't save reversed direction in a file: Unsupported codec!");
2475 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2476 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2477 "Can't save reversed direction in a file: Wrong length of captured packets!");
2478 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2479 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2480 "Can't save reversed direction in a file: RTP data with padding!");
2481 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2482 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2483 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2484 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2485 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2486 "Can't save reversed direction in a file: No RTP data!");
2488 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2489 "Can't save reversed direction in a file: File I/O problem!");
2493 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2494 format = SAVE_WAV_FORMAT;
2495 else */if (GTK_TOGGLE_BUTTON (au)->active)
2496 format = SAVE_AU_FORMAT;
2497 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2498 format = SAVE_SW_FORMAT;*/
2499 else if (GTK_TOGGLE_BUTTON (raw)->active)
2500 format = SAVE_RAW_FORMAT;
2502 format = SAVE_NONE_FORMAT;
2504 if (GTK_TOGGLE_BUTTON (rev)->active)
2505 channels = SAVE_REVERSE_DIRECTION_MASK;
2506 else if (GTK_TOGGLE_BUTTON (both)->active)
2507 channels = SAVE_BOTH_DIRECTION_MASK;
2509 channels = SAVE_FORWARD_DIRECTION_MASK;
2511 /* direction/format validity*/
2512 if (format == SAVE_AU_FORMAT)
2514 /* make sure streams are alaw/ulaw */
2515 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2516 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2517 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2520 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2521 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2522 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2525 /* make sure pt's don't differ */
2526 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2527 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2528 "Can't save in a file: Forward and reverse direction differ in type");
2532 else if (format == SAVE_RAW_FORMAT)
2534 /* can't save raw in both directions */
2535 if (channels == SAVE_BOTH_DIRECTION_MASK){
2536 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2537 "Can't save in a file: Unable to save raw data in both directions");
2543 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2544 "Can't save in a file: Invalid save format");
2548 if(!copy_file(g_dest, channels, format, user_data)) {
2549 /* XXX - report the error type! */
2550 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2551 "An error occurred while saving voice in a file!");
2555 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2558 /****************************************************************************/
2559 /* when the user wants to save the voice information in a file */
2560 /* XXX support for different formats is currently commented out */
2561 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2565 GtkWidget *label_format;
2566 GtkWidget *channels_label;
2567 GSList *format_group = NULL;
2568 GSList *channels_group = NULL;
2569 GtkWidget *forward_rb;
2570 GtkWidget *reversed_rb;
2572 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2577 /* if we can't save in a file: wrong codec, cut packets or other errors */
2578 /* shold the error arise here or later when you click ok button ?
2579 * if we do it here, then we must disable the refresh button, so we don't do it here */
2581 if (user_data->dlg.save_voice_as_w != NULL) {
2582 /* There's already a Save voice info dialog box; reactivate it. */
2583 reactivate_window(user_data->dlg.save_voice_as_w);
2587 /* XXX - use file_selection from dlg_utils instead! */
2588 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Wireshark: Save Payload As ...");
2590 /* Container for each row of widgets */
2591 vertb = gtk_vbox_new(FALSE, 0);
2592 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2593 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2594 vertb, FALSE, FALSE, 0);
2595 gtk_widget_show (vertb);
2597 table1 = gtk_table_new (2, 4, FALSE);
2598 gtk_widget_show (table1);
2599 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2600 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2601 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2603 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2604 gtk_widget_show (label_format);
2605 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2606 (GtkAttachOptions) (GTK_FILL),
2607 (GtkAttachOptions) (0), 0, 0);*/
2609 label_format = gtk_label_new ("Format: ");
2610 gtk_widget_show (label_format);
2611 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2612 (GtkAttachOptions) (GTK_FILL),
2613 (GtkAttachOptions) (0), 0, 0);
2615 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
2617 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2618 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (raw_rb));
2619 gtk_widget_show (raw_rb);
2620 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2621 (GtkAttachOptions) (GTK_FILL),
2622 (GtkAttachOptions) (0), 0, 0);
2625 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2626 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2627 gtk_widget_show (au_rb);
2628 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2629 (GtkAttachOptions) (GTK_FILL),
2630 (GtkAttachOptions) (0), 0, 0);
2632 /* we support .au - ulaw*/
2633 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2634 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
2635 gtk_widget_show (wav_rb);
2636 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2637 (GtkAttachOptions) (GTK_FILL),
2638 (GtkAttachOptions) (0), 0, 0);
2640 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2641 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
2642 gtk_widget_show (sw_rb);
2643 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2644 (GtkAttachOptions) (GTK_FILL),
2645 (GtkAttachOptions) (0), 0, 0);
2646 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2647 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2648 gtk_widget_show (au_rb);
2649 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2650 (GtkAttachOptions) (GTK_FILL),
2651 (GtkAttachOptions) (0), 0, 0);
2655 channels_label = gtk_label_new ("Channels:");
2656 gtk_widget_show (channels_label);
2657 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2658 (GtkAttachOptions) (GTK_FILL),
2659 (GtkAttachOptions) (0), 0, 0);
2660 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2662 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2663 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2664 gtk_widget_show (forward_rb);
2665 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2666 (GtkAttachOptions) (GTK_FILL),
2667 (GtkAttachOptions) (0), 0, 0);
2669 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2670 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2671 gtk_widget_show (reversed_rb);
2672 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2673 (GtkAttachOptions) (GTK_FILL),
2674 (GtkAttachOptions) (0), 0, 0);
2676 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2677 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2678 gtk_widget_show (both_rb);
2679 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2680 (GtkAttachOptions) (GTK_FILL),
2681 (GtkAttachOptions) (0), 0, 0);
2683 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2685 /* if one direction is nok we don't allow saving
2686 XXX this is not ok since the user can click the refresh button and cause changes
2687 but we can not update this window. So we move all the decision on the time the ok
2689 if (user_data->forward.saved == FALSE) {
2690 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2691 gtk_widget_set_sensitive(forward_rb, FALSE);
2692 gtk_widget_set_sensitive(both_rb, FALSE);
2694 else if (user_data->reversed.saved == FALSE) {
2695 gtk_widget_set_sensitive(reversed_rb, FALSE);
2696 gtk_widget_set_sensitive(both_rb, FALSE);
2700 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2701 /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2702 g_object_set_data(G_OBJECT(ok_bt), "au_rb", au_rb);
2703 /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2704 g_object_set_data(G_OBJECT(ok_bt), "raw_rb", raw_rb);
2705 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2706 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2707 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2708 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2709 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_voice_as_ok_cb),
2710 user_data->dlg.save_voice_as_w);
2712 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2713 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2715 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2716 G_CALLBACK(window_delete_event_cb), NULL);
2717 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2718 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2720 gtk_widget_show(user_data->dlg.save_voice_as_w);
2721 window_present(user_data->dlg.save_voice_as_w);
2725 /****************************************************************************/
2726 /* when we are finished with redisection, we add the label for the statistic */
2727 static void draw_stat(user_data_t *user_data)
2729 gchar label_max[200];
2730 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2731 - user_data->forward.statinfo.start_seq_nr + 1;
2732 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2733 - user_data->reversed.statinfo.start_seq_nr + 1;
2734 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2735 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2736 double f_perc, r_perc;
2738 f_perc = (double)(f_lost*100)/(double)f_expected;
2743 r_perc = (double)(r_lost*100)/(double)r_expected;
2748 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2749 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2750 " Sequence errors = %u",
2751 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2752 user_data->forward.statinfo.total_nr,
2753 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2755 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2757 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2758 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2759 " Sequence errors = %u",
2760 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2761 user_data->reversed.statinfo.total_nr,
2762 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2764 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2771 /****************************************************************************/
2772 /* append a line to clist */
2773 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
2774 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2775 gchar *timeStr, guint32 pkt_len, GdkColor *color)
2782 data[0]=&field[0][0];
2783 data[1]=&field[1][0];
2784 data[2]=&field[2][0];
2785 data[3]=&field[3][0];
2786 data[4]=&field[4][0];
2787 data[5]=&field[5][0];
2788 data[6]=&field[6][0];
2789 data[7]=&field[7][0];
2790 data[8]=&field[8][0];
2792 /* save the current locale */
2793 savelocale = setlocale(LC_NUMERIC, NULL);
2794 /* switch to "C" locale to avoid problems with localized decimal separators
2795 in g_snprintf("%f") functions */
2796 setlocale(LC_NUMERIC, "C");
2797 g_snprintf(field[0], 20, "%u", number);
2798 g_snprintf(field[1], 20, "%u", seq_num);
2799 g_snprintf(field[2], 20, "%.2f", delta);
2800 g_snprintf(field[3], 20, "%.2f", jitter);
2801 g_snprintf(field[4], 20, "%.2f", bandwidth);
2802 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
2803 g_snprintf(field[6], 40, "%s", status);
2804 g_snprintf(field[7], 32, "%s", timeStr);
2805 g_snprintf(field[8], 20, "%u", pkt_len);
2806 /* restore previous locale setting */
2807 setlocale(LC_NUMERIC, savelocale);
2809 added_row = gtk_clist_append(GTK_CLIST(clist), data);
2810 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
2811 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
2815 /****************************************************************************/
2816 /* callback for sorting columns of clist */
2817 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
2824 const GtkCListRow *row1 = ptr1;
2825 const GtkCListRow *row2 = ptr2;
2827 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
2828 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
2830 switch(clist->sort_column){
2831 /* columns representing strings */
2835 return strcmp (text1, text2);
2836 /* columns representing ints */
2843 /* columns representing floats */
2849 if (fabs(f1-f2)<0.0000005)
2855 g_assert_not_reached();
2860 /****************************************************************************/
2862 click_column_cb(GtkCList *clist, gint column, gpointer data)
2864 column_arrows *col_arrows = (column_arrows *) data;
2867 gtk_clist_freeze(clist);
2869 for (i = 0; i < NUM_COLS; i++) {
2870 gtk_widget_hide(col_arrows[i].ascend_pm);
2871 gtk_widget_hide(col_arrows[i].descend_pm);
2874 if (column == clist->sort_column) {
2875 if (clist->sort_type == GTK_SORT_ASCENDING) {
2876 clist->sort_type = GTK_SORT_DESCENDING;
2877 gtk_widget_show(col_arrows[column].descend_pm);
2879 clist->sort_type = GTK_SORT_ASCENDING;
2880 gtk_widget_show(col_arrows[column].ascend_pm);
2883 clist->sort_type = GTK_SORT_ASCENDING;
2884 gtk_widget_show(col_arrows[column].ascend_pm);
2885 gtk_clist_set_sort_column(clist, column);
2887 gtk_clist_thaw(clist);
2889 gtk_clist_sort(clist);
2893 /****************************************************************************/
2894 /* Add the packet list */
2896 GtkWidget* create_clist(user_data_t* user_data)
2898 GtkWidget* clist_fwd;
2900 /* clist for the information */
2901 clist_fwd = gtk_clist_new(NUM_COLS);
2902 gtk_widget_show(clist_fwd);
2903 g_signal_connect(clist_fwd, "select_row", G_CALLBACK(on_clist_select_row), user_data);
2905 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
2906 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
2907 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
2908 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
2910 /* hide date and length column */
2911 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
2912 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
2914 /* column widths and justification */
2915 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
2916 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
2917 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
2918 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
2919 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
2920 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
2921 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
2922 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
2923 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
2924 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
2925 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
2926 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
2927 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
2932 /****************************************************************************/
2933 /* Add the sort by column feature for a packet clist */
2935 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
2936 user_data_t* user_data _U_)
2938 column_arrows *col_arrows;
2939 GdkBitmap *ascend_bm, *descend_bm;
2940 GdkPixmap *ascend_pm, *descend_pm;
2941 GtkStyle *win_style;
2942 GtkWidget *column_lb;
2945 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
2946 win_style = gtk_widget_get_style(window);
2947 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
2949 &win_style->bg[GTK_STATE_NORMAL],
2950 (gchar **)clist_ascend_xpm);
2951 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
2953 &win_style->bg[GTK_STATE_NORMAL],
2954 (gchar **)clist_descend_xpm);
2956 for (i=0; i<NUM_COLS; i++) {
2957 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
2958 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
2959 column_lb = gtk_label_new(titles[i]);
2960 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
2961 gtk_widget_show(column_lb);
2963 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
2964 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
2965 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
2966 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
2967 /* make packet-nr be the default sort order */
2969 gtk_widget_show(col_arrows[i].ascend_pm);
2971 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
2972 gtk_widget_show(col_arrows[i].table);
2975 g_signal_connect(clist, "click-column", G_CALLBACK(click_column_cb), col_arrows);
2980 /****************************************************************************/
2981 /* Create the dialog box with all widgets */
2982 static void create_rtp_dialog(user_data_t* user_data)
2984 GtkWidget *window = NULL;
2985 GtkWidget *clist_fwd;
2986 GtkWidget *clist_rev;
2987 GtkWidget *label_stats_fwd;
2988 GtkWidget *label_stats_rev;
2989 GtkWidget *notebook;
2991 GtkWidget *main_vb, *page, *page_r;
2993 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
2994 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
2995 #ifdef USE_CONVERSATION_GRAPH
2996 GtkWidget *graph_bt;
2998 GtkWidget *graph_bt;
2999 gchar label_forward[150];
3000 gchar label_reverse[150];
3002 gchar str_ip_src[16];
3003 gchar str_ip_dst[16];
3004 column_arrows *col_arrows_fwd;
3005 column_arrows *col_arrows_rev;
3007 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
3008 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3010 /* Container for each row of widgets */
3011 main_vb = gtk_vbox_new(FALSE, 2);
3012 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3013 gtk_container_add(GTK_CONTAINER(window), main_vb);
3014 gtk_widget_show(main_vb);
3017 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3018 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3020 g_snprintf(label_forward, 149,
3021 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3022 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3025 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3026 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3028 g_snprintf(label_reverse, 149,
3029 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3030 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3032 /* Start a notebook for flipping between sets of changes */
3033 notebook = gtk_notebook_new();
3034 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3035 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3037 user_data->dlg.notebook_signal_id =
3038 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3040 /* page for forward connection */
3041 page = gtk_vbox_new(FALSE, 8);
3042 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3044 /* direction label */
3045 label = gtk_label_new(label_forward);
3046 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3048 /* place for some statistics */
3049 label_stats_fwd = gtk_label_new("\n");
3050 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3052 /* scrolled window */
3053 scrolled_window = scrolled_window_new(NULL, NULL);
3056 clist_fwd = create_clist(user_data);
3057 gtk_widget_show(clist_fwd);
3058 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3059 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3060 gtk_widget_show(scrolled_window);
3063 label = gtk_label_new(" Forward Direction ");
3064 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3066 /* same page for reversed connection */
3067 page_r = gtk_vbox_new(FALSE, 8);
3068 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3069 label = gtk_label_new(label_reverse);
3070 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3071 label_stats_rev = gtk_label_new("\n");
3072 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3074 scrolled_window_r = scrolled_window_new(NULL, NULL);
3076 clist_rev = create_clist(user_data);
3077 gtk_widget_show(clist_rev);
3078 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3079 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3080 gtk_widget_show(scrolled_window_r);
3082 label = gtk_label_new(" Reversed Direction ");
3083 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3085 /* page for help&about or future
3086 page_help = gtk_hbox_new(FALSE, 5);
3087 label = gtk_label_new(" Future ");
3088 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3089 frame = gtk_frame_new("");
3090 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3091 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3092 gtk_container_add(GTK_CONTAINER(frame), text);
3093 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3094 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3097 /* show all notebooks */
3098 gtk_widget_show_all(notebook);
3101 box4 = gtk_hbutton_box_new();
3102 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3103 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3104 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3105 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3106 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3107 gtk_widget_show(box4);
3109 voice_bt = gtk_button_new_with_label("Save payload...");
3110 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3111 gtk_widget_show(voice_bt);
3112 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3114 csv_bt = gtk_button_new_with_label("Save as CSV...");
3115 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3116 gtk_widget_show(csv_bt);
3117 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3119 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3120 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3121 gtk_widget_show(refresh_bt);
3122 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3124 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3125 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3126 gtk_widget_show(goto_bt);
3127 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3129 graph_bt = gtk_button_new_with_label("Graph");
3130 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3131 gtk_widget_show(graph_bt);
3132 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3135 #ifdef USE_CONVERSATION_GRAPH
3136 graph_bt = gtk_button_new_with_label("Graph");
3137 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3138 gtk_widget_show(graph_bt);
3139 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3142 next_bt = gtk_button_new_with_label("Next non-Ok");
3143 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3144 gtk_widget_show(next_bt);
3145 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3147 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3148 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3149 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3150 gtk_widget_show(close_bt);
3151 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3153 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3154 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3156 gtk_widget_show(window);
3157 window_present(window);
3159 /* sort by column feature */
3160 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3161 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3163 /* some widget references need to be saved for outside use */
3164 user_data->dlg.window = window;
3165 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3166 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3167 user_data->dlg.label_stats_fwd = label_stats_fwd;
3168 user_data->dlg.label_stats_rev = label_stats_rev;
3169 user_data->dlg.notebook = notebook;
3170 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3171 user_data->dlg.selected_row = 0;
3172 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3173 user_data->dlg.col_arrows_rev = col_arrows_rev;
3177 /****************************************************************************/
3178 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3179 const gchar* proto_field, guint32* p_result)
3182 proto_node *proto_sibling_node;
3183 header_field_info *hfssrc;
3186 finfo = PITEM_FINFO(ptree_node);
3188 if (hfinformation==(finfo->hfinfo)) {
3189 hfssrc = proto_registrar_get_byname(proto_field);
3192 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3193 ptree_node=ptree_node->next) {
3194 finfo=PITEM_FINFO(ptree_node);
3195 if (hfssrc==finfo->hfinfo) {
3196 if (hfinformation->type==FT_IPv4) {
3197 ipv4 = fvalue_get(&finfo->value);
3198 *p_result = ipv4_get_net_order_addr(ipv4);
3201 *p_result = fvalue_get_uinteger(&finfo->value);
3210 proto_sibling_node = ptree_node->next;
3212 if (proto_sibling_node) {
3213 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3219 /****************************************************************************/
3220 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3221 const gchar* proto_name,
3222 const gchar* proto_field,
3225 proto_node *ptree_node;
3226 header_field_info *hfinformation;
3228 hfinformation = proto_registrar_get_byname(proto_name);
3229 if (hfinformation == NULL)
3232 ptree_node = ((proto_node *)protocol_tree)->first_child;
3236 return process_node(ptree_node, hfinformation, proto_field, p_result);
3240 /* XXX just copied from gtk/rpc_stat.c */
3241 void protect_thread_critical_region(void);
3242 void unprotect_thread_critical_region(void);
3244 /****************************************************************************/
3246 address *ip_src_fwd,
3247 guint16 port_src_fwd,
3248 address *ip_dst_fwd,
3249 guint16 port_dst_fwd,
3251 address *ip_src_rev,
3252 guint16 port_src_rev,
3253 address *ip_dst_rev,
3254 guint16 port_dst_rev,
3258 user_data_t *user_data;
3261 static color_t col[MAX_GRAPHS] = {
3262 {0, 0x0000, 0x0000, 0x0000},
3263 {0, 0xffff, 0x0000, 0x0000},
3264 {0, 0x0000, 0xffff, 0x0000},
3265 {0, 0x0000, 0x0000, 0xffff}
3269 user_data = g_malloc(sizeof(user_data_t));
3271 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3272 user_data->port_src_fwd = port_src_fwd;
3273 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3274 user_data->port_dst_fwd = port_dst_fwd;
3275 user_data->ssrc_fwd = ssrc_fwd;
3276 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3277 user_data->port_src_rev = port_src_rev;
3278 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3279 user_data->port_dst_rev = port_dst_rev;
3280 user_data->ssrc_rev = ssrc_rev;
3283 /* file names for storing sound data */
3284 /*XXX: check for errors*/
3285 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3288 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3291 user_data->forward.saveinfo.fp = NULL;
3292 user_data->reversed.saveinfo.fp = NULL;
3293 user_data->dlg.save_voice_as_w = NULL;
3294 user_data->dlg.save_csv_as_w = NULL;
3295 user_data->dlg.dialog_graph.window = NULL;
3297 #ifdef USE_CONVERSATION_GRAPH
3298 user_data->dlg.graph_window = NULL;
3299 user_data->series_fwd.value_pairs = NULL;
3300 user_data->series_rev.value_pairs = NULL;
3303 /* init dialog_graph */
3304 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3305 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3306 user_data->dlg.dialog_graph.draw_area=NULL;
3307 user_data->dlg.dialog_graph.pixmap=NULL;
3308 user_data->dlg.dialog_graph.scrollbar=NULL;
3309 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3310 user_data->dlg.dialog_graph.pixmap_width=500;
3311 user_data->dlg.dialog_graph.pixmap_height=200;
3312 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3313 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3314 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3315 user_data->dlg.dialog_graph.max_interval=0;
3316 user_data->dlg.dialog_graph.num_items=0;
3317 user_data->dlg.dialog_graph.start_time = -1;
3319 for(i=0;i<MAX_GRAPHS;i++){
3320 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3321 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3322 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3323 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3324 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3325 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3326 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3327 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3330 /* create the dialog box */
3331 create_rtp_dialog(user_data);
3333 /* proceed as if the Refresh button would have been pressed */
3334 on_refresh_bt_clicked(NULL, user_data);
3337 /****************************************************************************/
3338 /* entry point from main menu */
3339 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3342 guint16 port_src_fwd;
3344 guint16 port_dst_fwd;
3345 guint32 ssrc_fwd = 0;
3347 guint16 port_src_rev;
3349 guint16 port_dst_rev;
3350 guint32 ssrc_rev = 0;
3351 unsigned int version_fwd;
3353 gchar filter_text[256];
3356 epan_dissect_t *edt;
3359 gboolean frame_matched;
3361 GList *strinfo_list;
3362 GList *filtered_list = NULL;
3363 rtp_stream_info_t *strinfo;
3366 /* Try to compile the filter. */
3367 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",256);
3368 if (!dfilter_compile(filter_text, &sfcode)) {
3369 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3372 /* we load the current file into cf variable */
3374 fdata = cf->current_frame;
3376 /* we are on the selected frame now */
3378 return; /* if we exit here it's an error */
3380 /* dissect the current frame */
3381 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3382 cf->pd, fdata->cap_len, &err, &err_info)) {
3383 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3384 cf_read_error_message(err, err_info), cf->filename);
3387 edt = epan_dissect_new(TRUE, FALSE);
3388 epan_dissect_prime_dfilter(edt, sfcode);
3389 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3390 frame_matched = dfilter_apply_edt(sfcode, edt);
3392 /* if it is not an rtp frame, show the rtpstream dialog */
3393 frame_matched = dfilter_apply_edt(sfcode, edt);
3394 if (frame_matched != 1) {
3395 epan_dissect_free(edt);
3396 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3397 "You didn't choose a RTP packet!");
3401 /* ok, it is a RTP frame, so let's get the ip and port values */
3402 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3403 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3404 port_src_fwd = edt->pi.srcport;
3405 port_dst_fwd = edt->pi.destport;
3407 /* assume the inverse ip/port combination for the reverse direction */
3408 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3409 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3410 port_src_rev = edt->pi.destport;
3411 port_dst_rev = edt->pi.srcport;
3413 /* check if it is RTP Version 2 */
3414 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3415 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3416 "RTP Version != 2 isn't supported!");
3420 /* now we need the SSRC value of the current frame */
3421 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3422 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3423 "SSRC value couldn't be found!");
3427 /* Scan for rtpstream */
3429 /* search for reversed direction in the global rtp streams list */
3431 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3432 while (strinfo_list)
3434 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3435 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3436 && strinfo->src_port==port_src_fwd
3437 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3438 && strinfo->dest_port==port_dst_fwd)
3440 filtered_list = g_list_prepend(filtered_list, strinfo);
3443 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3444 && strinfo->src_port==port_src_rev
3445 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3446 && strinfo->dest_port==port_dst_rev)
3449 filtered_list = g_list_append(filtered_list, strinfo);
3451 ssrc_rev = strinfo->ssrc;
3454 strinfo_list = g_list_next(strinfo_list);
3457 /* if more than one reverse streams found, we let the user choose the right one */
3459 rtpstream_dlg_show(filtered_list);
3478 /****************************************************************************/
3480 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3482 rtp_analysis_cb(NULL, NULL);
3485 /****************************************************************************/
3487 register_tap_listener_rtp_analysis(void)
3489 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3491 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3492 rtp_analysis_cb, NULL, NULL, NULL);