2 * RTP analysis addition for ethereal
4 * $Id: rtp_analysis.c,v 1.29 2004/01/31 02:25:46 ulfl Exp $
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 * Ethereal - Network traffic analyzer
14 * By Gerald Combs <gerald@ethereal.com>
15 * Copyright 1998 Gerald Combs
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 /*do not define this symbol. will be added soon*/
37 /*#define USE_CONVERSATION_GRAPH 1*/
39 #include "rtp_analysis.h"
40 #include "rtp_stream.h"
41 #include "rtp_stream_dlg.h"
43 #ifdef USE_CONVERSATION_GRAPH
44 #include "../graph/graph.h"
48 #include "epan/epan_dissect.h"
49 #include "epan/filesystem.h"
52 #include "packet-rtp.h"
61 #include "dlg_utils.h"
63 #include "simple_dialog.h"
66 #include "progress_dlg.h"
67 #include "compat_macros.h"
69 #include "image/clist_ascend.xpm"
70 #include "image/clist_descend.xpm"
81 #include <io.h> /* open/close on win32 */
88 /****************************************************************************/
90 typedef struct column_arrows {
93 GtkWidget *descend_pm;
96 typedef struct _dialog_data_t {
100 GtkWidget *label_stats_fwd;
101 GtkWidget *label_stats_rev;
102 column_arrows *col_arrows_fwd;
103 column_arrows *col_arrows_rev;
105 GtkCList *selected_clist;
106 GtkWidget *save_voice_as_w;
107 GtkWidget *save_csv_as_w;
109 #ifdef USE_CONVERSATION_GRAPH
110 GtkWidget *graph_window;
114 #define OK_TEXT "[ Ok ]"
115 #define PT_UNDEFINED -1
117 /* type of error when saving voice in a file didn't succeed */
120 TAP_RTP_WRONG_LENGTH,
121 TAP_RTP_PADDING_ERROR,
122 TAP_RTP_FILE_OPEN_ERROR,
127 /****************************************************************************/
128 /* structure that holds the information about the forward and reversed direction */
129 typedef struct _tap_rtp_stat_t {
130 gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
131 /* use (flags & STAT_FLAG_FIRST) instead */
132 /* all of the following fields will be initialized after
133 rtp_packet_analyse has been called */
134 guint32 flags; /* see STAT_FLAG-defines below */
137 guint32 delta_timestamp;
144 guint16 start_seq_nr;
154 /* status flags for the flags parameter in tap_rtp_stat_t */
155 #define STAT_FLAG_FIRST 0x01
156 #define STAT_FLAG_MARKER 0x02
157 #define STAT_FLAG_WRONG_SEQ 0x04
158 #define STAT_FLAG_PT_CHANGE 0x08
159 #define STAT_FLAG_PT_CN 0x10
160 #define STAT_FLAG_FOLLOW_PT_CN 0x20
161 #define STAT_FLAG_REG_PT_CHANGE 0x40
163 typedef struct _tap_rtp_save_info_t {
166 error_type_t error_type;
168 } tap_rtp_save_info_t;
171 /* structure that holds the information about the forward and reversed direction */
172 struct _info_direction {
173 tap_rtp_stat_t statinfo;
174 tap_rtp_save_info_t saveinfo;
177 #define TMPNAMSIZE 100
179 /* structure that holds general information about the connection
180 * and structures for both directions */
181 typedef struct _user_data_t {
182 /* tap associated data*/
184 guint16 port_src_fwd;
186 guint16 port_dst_fwd;
189 guint16 port_src_rev;
191 guint16 port_dst_rev;
194 struct _info_direction forward;
195 struct _info_direction reversed;
197 char f_tempname[TMPNAMSIZE];
198 char r_tempname[TMPNAMSIZE];
200 /* dialog associated data */
203 #ifdef USE_CONVERSATION_GRAPH
204 time_series_t series_fwd;
205 time_series_t series_rev;
211 static gchar *titles[8] = {
223 typedef const guint8 * ip_addr_p;
226 /****************************************************************************/
229 /****************************************************************************/
230 /* when there is a [re]reading of packet's */
232 rtp_reset(void *user_data_arg)
234 user_data_t *user_data = user_data_arg;
236 user_data->forward.statinfo.first_packet = TRUE;
237 user_data->reversed.statinfo.first_packet = TRUE;
238 user_data->forward.statinfo.max_delay = 0;
239 user_data->reversed.statinfo.max_delay = 0;
240 user_data->forward.statinfo.delay = 0;
241 user_data->reversed.statinfo.delay = 0;
242 user_data->forward.statinfo.jitter = 0;
243 user_data->reversed.statinfo.jitter = 0;
244 user_data->forward.statinfo.timestamp = 0;
245 user_data->reversed.statinfo.timestamp = 0;
246 user_data->forward.statinfo.max_nr = 0;
247 user_data->reversed.statinfo.max_nr = 0;
248 user_data->forward.statinfo.total_nr = 0;
249 user_data->reversed.statinfo.total_nr = 0;
250 user_data->forward.statinfo.sequence = 0;
251 user_data->reversed.statinfo.sequence = 0;
252 user_data->forward.statinfo.start_seq_nr = 0;
253 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
254 user_data->forward.statinfo.stop_seq_nr = 0;
255 user_data->reversed.statinfo.stop_seq_nr = 0;
256 user_data->forward.statinfo.cycles = 0;
257 user_data->reversed.statinfo.cycles = 0;
258 user_data->forward.statinfo.under = FALSE;
259 user_data->reversed.statinfo.under = FALSE;
260 user_data->forward.statinfo.start_time = 0;
261 user_data->reversed.statinfo.start_time = 0;
262 user_data->forward.statinfo.time = 0;
263 user_data->reversed.statinfo.time = 0;
264 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
265 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
267 user_data->forward.saveinfo.count = 0;
268 user_data->reversed.saveinfo.count = 0;
269 user_data->forward.saveinfo.saved = FALSE;
270 user_data->reversed.saveinfo.saved = FALSE;
272 #ifdef USE_CONVERSATION_GRAPH
273 if (user_data->dlg.graph_window != NULL)
274 gtk_widget_destroy(user_data->dlg.graph_window);
276 g_array_free(user_data->series_fwd.value_pairs, TRUE);
277 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
279 g_array_free(user_data->series_rev.value_pairs, TRUE);
280 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
283 /* XXX check for error at fclose? */
284 if (user_data->forward.saveinfo.fp != NULL)
285 fclose(user_data->forward.saveinfo.fp);
286 if (user_data->reversed.saveinfo.fp != NULL)
287 fclose(user_data->reversed.saveinfo.fp);
288 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
289 if (user_data->forward.saveinfo.fp == NULL)
290 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
291 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
292 if (user_data->reversed.saveinfo.fp == NULL)
293 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
297 /****************************************************************************/
298 /* here we can redraw the output */
300 static void rtp_draw(void *prs _U_)
305 /* forward declarations */
306 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
307 double delay, double jitter, gchar *status, gboolean marker,
308 gchar *timeStr, guint32 pkt_len, GdkColor *color);
310 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
311 packet_info *pinfo, struct _rtp_info *rtpinfo);
312 static int rtp_packet_add_info(GtkCList *clist,
313 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
314 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
315 tap_rtp_stat_t *statinfo,
316 packet_info *pinfo, struct _rtp_info *rtpinfo);
319 /****************************************************************************/
320 /* whenever a RTP packet is seen by the tap listener */
321 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *rtpinfo_arg)
323 user_data_t *user_data = user_data_arg;
324 struct _rtp_info *rtpinfo = rtpinfo_arg;
325 #ifdef USE_CONVERSATION_GRAPH
329 /* we ignore packets that are not displayed */
330 if (pinfo->fd->flags.passed_dfilter == 0)
333 /* is it the forward direction? */
334 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
335 #ifdef USE_CONVERSATION_GRAPH
336 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
337 vp.fnumber = pinfo->fd->num;
338 g_array_append_val(user_data->series_fwd.value_pairs, vp);
340 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
341 rtp_packet_add_info(user_data->dlg.clist_fwd,
342 &(user_data->forward.statinfo), pinfo, rtpinfo);
343 rtp_packet_save_payload(&(user_data->forward.saveinfo),
344 &(user_data->forward.statinfo), pinfo, rtpinfo);
346 /* is it the reversed direction? */
347 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
348 #ifdef USE_CONVERSATION_GRAPH
349 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
350 vp.fnumber = pinfo->fd->num;
351 g_array_append_val(user_data->series_rev.value_pairs, vp);
353 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
354 rtp_packet_add_info(user_data->dlg.clist_rev,
355 &(user_data->reversed.statinfo), pinfo, rtpinfo);
356 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
357 &(user_data->reversed.statinfo), pinfo, rtpinfo);
364 /****************************************************************************/
365 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
366 packet_info *pinfo, struct _rtp_info *rtpinfo)
369 double current_jitter;
373 /* check payload type */
374 if (rtpinfo->info_payload_type == PT_CN
375 || rtpinfo->info_payload_type == PT_CN_OLD)
376 statinfo->flags |= STAT_FLAG_PT_CN;
377 if (statinfo->pt == PT_CN
378 || statinfo->pt == PT_CN_OLD)
379 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
380 if (rtpinfo->info_payload_type != statinfo->pt)
381 statinfo->flags |= STAT_FLAG_PT_CHANGE;
383 statinfo->pt = rtpinfo->info_payload_type;
385 /* store the current time and calculate the current jitter */
386 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
387 current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
388 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
389 statinfo->delay = current_time-(statinfo->time);
390 statinfo->jitter = current_jitter;
392 /* is this the first packet we got in this direction? */
393 if (statinfo->first_packet) {
394 statinfo->start_seq_nr = rtpinfo->info_seq_num;
395 statinfo->start_time = current_time;
397 statinfo->jitter = 0;
398 statinfo->flags |= STAT_FLAG_FIRST;
399 statinfo->first_packet = FALSE;
401 /* is it a packet with the mark bit set? */
402 if (rtpinfo->info_marker_set) {
403 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
404 statinfo->flags |= STAT_FLAG_MARKER;
406 /* is it a regular packet? */
407 if (!(statinfo->flags & STAT_FLAG_FIRST)
408 && !(statinfo->flags & STAT_FLAG_MARKER)
409 && !(statinfo->flags & STAT_FLAG_PT_CN)
410 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
411 /* include it in maximum delay calculation */
412 if (statinfo->delay > statinfo->max_delay) {
413 statinfo->max_delay = statinfo->delay;
414 statinfo->max_nr = pinfo->fd->num;
417 /* regular payload change? (CN ignored) */
418 if (!(statinfo->flags & STAT_FLAG_FIRST)
419 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
420 if ((statinfo->pt != statinfo->reg_pt)
421 && (statinfo->reg_pt != PT_UNDEFINED)) {
422 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
426 /* set regular payload*/
427 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
428 statinfo->reg_pt = statinfo->pt;
432 /* When calculating expected rtp packets the seq number can wrap around
433 * so we have to count the number of cycles
434 * Variable cycles counts the wraps around in forwarding connection and
435 * under is flag that indicates where we are
437 * XXX how to determine number of cycles with all possible lost, late
438 * and duplicated packets without any doubt? It seems to me, that
439 * because of all possible combination of late, duplicated or lost
440 * packets, this can only be more or less good approximation
442 * There are some combinations (rare but theoretically possible),
443 * where below code won't work correctly - statistic may be wrong then.
446 /* so if the current sequence number is less than the start one
447 * we assume, that there is another cycle running */
448 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
450 statinfo->under = TRUE;
452 /* what if the start seq nr was 0? Then the above condition will never
453 * be true, so we add another condition. XXX The problem would arise
454 * if one of the packets with seq nr 0 or 65535 would be lost or late */
455 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
456 (statinfo->under == FALSE)){
458 statinfo->under = TRUE;
460 /* the whole round is over, so reset the flag */
461 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
462 statinfo->under = FALSE;
465 /* Since it is difficult to count lost, duplicate or late packets separately,
466 * we would like to know at least how many times the sequence number was not ok */
468 /* if the current seq number equals the last one or if we are here for
469 * the first time, then it is ok, we just store the current one as the last one */
470 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
471 statinfo->seq_num = rtpinfo->info_seq_num;
472 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
473 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
474 statinfo->seq_num = rtpinfo->info_seq_num;
476 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
477 statinfo->seq_num = rtpinfo->info_seq_num;
478 statinfo->sequence++;
479 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
481 /* late or duplicated */
482 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
483 statinfo->sequence++;
484 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
487 statinfo->time = current_time;
488 statinfo->timestamp = rtpinfo->info_timestamp;
489 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
490 statinfo->total_nr++;
496 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
497 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
498 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
499 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
501 /****************************************************************************/
502 /* adds statistics information from the packet to the clist */
503 static int rtp_packet_add_info(GtkCList *clist,
504 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
511 GdkColor color = COLOR_DEFAULT;
513 then = pinfo->fd->abs_secs;
514 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
515 tm_tmp = localtime(&then);
516 snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
519 tm_tmp->tm_year + 1900,
525 if (statinfo->pt == PT_CN) {
526 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
529 else if (statinfo->pt == PT_CN_OLD) {
530 snprintf(status,40,"Comfort noise (PT=19, reserved)");
533 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
534 snprintf(status,40,"Wrong sequence nr.");
537 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
538 snprintf(status,40,"Payload changed to PT=%u", statinfo->pt);
539 color = COLOR_WARNING;
541 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
542 && !(statinfo->flags & STAT_FLAG_FIRST)
543 && !(statinfo->flags & STAT_FLAG_PT_CN)
544 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
545 && !(statinfo->flags & STAT_FLAG_MARKER)) {
546 snprintf(status,40,"Marker missing?");
547 color = COLOR_WARNING;
550 if (statinfo->flags & STAT_FLAG_MARKER) {
551 color = COLOR_WARNING;
553 snprintf(status,40,OK_TEXT);
556 /* is this the first packet we got in this direction? */
557 if (statinfo->flags & STAT_FLAG_FIRST) {
559 pinfo->fd->num, rtpinfo->info_seq_num,
563 rtpinfo->info_marker_set,
564 timeStr, pinfo->fd->pkt_len,
569 pinfo->fd->num, rtpinfo->info_seq_num,
573 rtpinfo->info_marker_set,
574 timeStr, pinfo->fd->pkt_len,
582 /****************************************************************************/
583 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
584 tap_rtp_stat_t *statinfo,
585 packet_info *pinfo, struct _rtp_info *rtpinfo)
591 /* is this the first packet we got in this direction? */
592 if (statinfo->flags & STAT_FLAG_FIRST) {
593 if (saveinfo->fp == NULL) {
594 saveinfo->saved = FALSE;
595 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
598 saveinfo->saved = TRUE;
601 /* save the voice information */
602 /* if there was already an error, we quit */
603 if (saveinfo->saved == FALSE)
606 /* if the captured length and packet length aren't equal, we quit
607 * because there is some information missing */
608 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
609 saveinfo->saved = FALSE;
610 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
614 /* if padding bit is set, but the padding count is bigger
615 * then the whole RTP data - error with padding count */
616 if ( (rtpinfo->info_padding_set != FALSE) &&
617 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
618 saveinfo->saved = FALSE;
619 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
623 /* do we need to insert some silence? */
624 if ((rtpinfo->info_marker_set) &&
625 !(statinfo->flags & STAT_FLAG_FIRST) &&
626 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
627 /* the amount of silence should be the difference between
628 * the last timestamp and the current one minus x
629 * x should equal the amount of information in the last frame
630 * XXX not done yet */
631 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
632 rtpinfo->info_padding_count); i++) {
633 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
634 fwrite(&tmp, 2, 1, saveinfo->fp);
637 fflush(saveinfo->fp);
641 if (rtpinfo->info_payload_type == PT_PCMU) {
642 /* we put the pointer at the beggining of the RTP data, that is
643 * at the end of the current frame minus the length of the
644 * padding count minus length of the RTP data */
645 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
646 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
647 tmp = (gint16 )ulaw2linear((unsigned char)*data);
648 fwrite(&tmp, 2, 1, saveinfo->fp);
651 fflush(saveinfo->fp);
652 saveinfo->saved = TRUE;
657 else if (rtpinfo->info_payload_type == PT_PCMA) {
658 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
659 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
660 tmp = (gint16 )alaw2linear((unsigned char)*data);
661 fwrite(&tmp, 2, 1, saveinfo->fp);
664 fflush(saveinfo->fp);
665 saveinfo->saved = TRUE;
668 /* comfort noise? - do nothing */
669 else if (rtpinfo->info_payload_type == PT_CN
670 || rtpinfo->info_payload_type == PT_CN_OLD) {
672 /* unsupported codec or XXX other error */
674 saveinfo->saved = FALSE;
675 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
683 /****************************************************************************/
686 /****************************************************************************/
687 /* XXX just copied from gtk/rpc_stat.c */
688 void protect_thread_critical_region(void);
689 void unprotect_thread_critical_region(void);
692 /****************************************************************************/
693 /* close the dialog window and remove the tap listener */
694 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
696 /* close and remove temporary files */
697 if (user_data->forward.saveinfo.fp != NULL)
698 fclose(user_data->forward.saveinfo.fp);
699 if (user_data->reversed.saveinfo.fp != NULL)
700 fclose(user_data->reversed.saveinfo.fp);
701 /*XXX: test for error **/
702 remove(user_data->f_tempname);
703 remove(user_data->r_tempname);
705 /* destroy save_voice_as window if open */
706 if (user_data->dlg.save_voice_as_w != NULL)
707 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
709 #ifdef USE_CONVERSATION_GRAPH
710 /* destroy graph window if open */
711 if (user_data->dlg.graph_window != NULL)
712 gtk_widget_destroy(user_data->dlg.graph_window);
715 g_free(user_data->dlg.col_arrows_fwd);
716 g_free(user_data->dlg.col_arrows_rev);
721 /****************************************************************************/
722 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
723 GtkNotebookPage *page _U_,
725 user_data_t *user_data _U_)
727 user_data->dlg.selected_clist =
728 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
729 user_data->dlg.selected_row = 0;
732 /****************************************************************************/
733 static void on_clist_select_row(GtkCList *clist _U_,
737 user_data_t *user_data _U_)
739 user_data->dlg.selected_clist = clist;
740 user_data->dlg.selected_row = row;
744 #ifdef USE_CONVERSATION_GRAPH
745 /****************************************************************************/
746 /* when the graph window gets destroyed */
747 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
749 /* note that graph window has been destroyed */
750 user_data->dlg.graph_window = NULL;
753 /****************************************************************************/
754 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
757 GtkCList *clist = NULL;
758 if (vp.fnumber != 0) {
759 clist = GTK_CLIST(user_data->dlg.clist_fwd);
760 row = gtk_clist_find_row_from_data(clist,
761 GUINT_TO_POINTER(vp.fnumber));
763 clist = GTK_CLIST(user_data->dlg.clist_rev);
764 row = gtk_clist_find_row_from_data(clist,
765 GUINT_TO_POINTER(vp.fnumber));
768 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
769 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
770 gtk_clist_select_row(clist, row, 0);
771 gtk_clist_moveto(clist, row, 0, 0.5, 0);
777 /****************************************************************************/
778 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
784 if (user_data->dlg.graph_window != NULL) {
785 /* There's already a graph window; reactivate it. */
786 reactivate_window(user_data->dlg.graph_window);
790 list = g_list_append(list, &(user_data->series_fwd));
791 list = g_list_append(list, &(user_data->series_rev));
793 user_data->series_fwd.color.pixel = 0;
794 user_data->series_fwd.color.red = 0x80ff;
795 user_data->series_fwd.color.green = 0xe0ff;
796 user_data->series_fwd.color.blue = 0xffff;
797 user_data->series_fwd.yvalue = 0.5;
799 user_data->series_rev.color.pixel = 0;
800 user_data->series_rev.color.red = 0x60ff;
801 user_data->series_rev.color.green = 0xc0ff;
802 user_data->series_rev.color.blue = 0xffff;
803 user_data->series_rev.yvalue = -0.5;
805 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
806 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
807 user_data->port_src_fwd,
808 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
809 user_data->port_dst_fwd,
810 user_data->ssrc_fwd);
812 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
813 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
814 user_data->port_src_rev,
815 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
816 user_data->port_dst_rev,
817 user_data->ssrc_rev);
819 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
820 &graph_selection_callback, user_data);
821 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
822 on_destroy_graph, user_data);
824 #endif /*USE_CONVERSATION_GRAPH*/
827 /****************************************************************************/
828 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
832 if (user_data->dlg.selected_clist!=NULL) {
833 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
834 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
835 goto_frame(&cfile, fnumber);
840 static void draw_stat(user_data_t *user_data);
842 /****************************************************************************/
843 /* re-dissects all packets */
844 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
846 gchar filter_text[256];
848 GString *error_string;
850 /* clear the dialog box clists */
851 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
852 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
854 /* try to compile the filter. */
855 strcpy(filter_text,"rtp && ip");
856 if (!dfilter_compile(filter_text, &sfcode)) {
857 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
861 sprintf(filter_text,"rtp && (( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ) || ( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ))",
862 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
863 user_data->port_src_fwd,
864 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
865 user_data->port_dst_fwd,
866 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
867 user_data->port_src_rev,
868 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
869 user_data->port_dst_rev
872 /* register tap listener */
873 error_string = register_tap_listener("rtp", user_data, filter_text,
874 rtp_reset, rtp_packet, rtp_draw);
875 if (error_string != NULL) {
876 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
877 g_string_free(error_string, TRUE);
881 /* retap all packets */
882 retap_packets(&cfile);
884 /* remove tap listener again */
885 protect_thread_critical_region();
886 remove_tap_listener(user_data);
887 unprotect_thread_critical_region();
889 /* draw statistics info */
890 draw_stat(user_data);
892 gtk_clist_sort(user_data->dlg.clist_fwd);
893 gtk_clist_sort(user_data->dlg.clist_rev);
896 /****************************************************************************/
897 /* on_destroy is automatically called after that */
898 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
900 gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
901 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
904 /****************************************************************************/
905 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
910 if (user_data->dlg.selected_clist==NULL)
913 clist = user_data->dlg.selected_clist;
914 row = user_data->dlg.selected_row + 1;
916 while (gtk_clist_get_text(clist,row,5,&text)) {
917 if (strcmp(text, OK_TEXT) != 0) {
918 gtk_clist_select_row(clist, row, 0);
919 gtk_clist_moveto(clist, row, 0, 0.5, 0);
927 while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
928 if (strcmp(text, OK_TEXT) != 0) {
929 gtk_clist_select_row(clist, row, 0);
930 gtk_clist_moveto(clist, row, 0, 0.5, 0);
937 /****************************************************************************/
938 /* when we want to save the information */
939 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
942 GtkWidget *rev, *forw, *both;
943 user_data_t *user_data;
949 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
951 /* Perhaps the user specified a directory instead of a file.
952 Check whether they did. */
953 if (test_for_directory(g_dest) == EISDIR) {
954 /* It's a directory - set the file selection box to display it. */
955 set_last_open_dir(g_dest);
957 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
961 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
962 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
963 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
964 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
966 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
967 fp = fopen(g_dest, "w");
969 simple_dialog(ESD_TYPE_ERROR, NULL,
970 file_open_error_message(errno, TRUE), g_dest);
974 if (GTK_TOGGLE_BUTTON(both)->active) {
975 fprintf(fp, "Forward\n");
977 simple_dialog(ESD_TYPE_ERROR, NULL,
978 file_write_error_message(errno), g_dest);
984 for(j = 0; j < 8; j++) {
986 fprintf(fp,"%s",titles[j]);
988 fprintf(fp,",%s",titles[j]);
993 simple_dialog(ESD_TYPE_ERROR, NULL,
994 file_write_error_message(errno), g_dest);
998 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
999 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1000 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1002 fprintf(fp,"%s",columnText);
1004 fprintf(fp,",%s",columnText);
1009 simple_dialog(ESD_TYPE_ERROR, NULL,
1010 file_write_error_message(errno), g_dest);
1016 if (fclose(fp) == EOF) {
1017 simple_dialog(ESD_TYPE_ERROR, NULL,
1018 file_write_error_message(errno), g_dest);
1023 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1025 if (GTK_TOGGLE_BUTTON(both)->active) {
1026 fp = fopen(g_dest, "a");
1028 simple_dialog(ESD_TYPE_ERROR, NULL,
1029 file_open_error_message(errno, TRUE),
1033 fprintf(fp, "\nReverse\n");
1035 simple_dialog(ESD_TYPE_ERROR, NULL,
1036 file_write_error_message(errno), g_dest);
1041 fp = fopen(g_dest, "w");
1043 simple_dialog(ESD_TYPE_ERROR, NULL,
1044 file_open_error_message(errno, TRUE),
1049 for(j = 0; j < 8; j++) {
1051 fprintf(fp,"%s",titles[j]);
1053 fprintf(fp,",%s",titles[j]);
1058 simple_dialog(ESD_TYPE_ERROR, NULL,
1059 file_write_error_message(errno), g_dest);
1063 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1064 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1065 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1067 fprintf(fp,"%s",columnText);
1069 fprintf(fp,",%s",columnText);
1074 simple_dialog(ESD_TYPE_ERROR, NULL,
1075 file_write_error_message(errno), g_dest);
1080 if (fclose(fp) == EOF) {
1081 simple_dialog(ESD_TYPE_ERROR, NULL,
1082 file_write_error_message(errno), g_dest);
1087 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1090 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1092 user_data->dlg.save_csv_as_w = NULL;
1095 /* when the user wants to save the csv information in a file */
1096 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
1100 GtkWidget *label_format;
1101 GtkWidget *channels_label;
1102 GSList *channels_group = NULL;
1103 GtkWidget *forward_rb;
1104 GtkWidget *reversed_rb;
1108 if (user_data->dlg.save_csv_as_w != NULL) {
1109 /* There's already a Save CSV info dialog box; reactivate it. */
1110 reactivate_window(user_data->dlg.save_csv_as_w);
1114 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
1115 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
1116 save_csv_as_destroy_cb, user_data);
1118 /* Container for each row of widgets */
1119 vertb = gtk_vbox_new(FALSE, 0);
1120 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1121 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
1122 vertb, FALSE, FALSE, 0);
1123 gtk_widget_show (vertb);
1125 table1 = gtk_table_new (2, 4, FALSE);
1126 gtk_widget_show (table1);
1127 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1128 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1129 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1131 label_format = gtk_label_new ("Format: Comma Separated Values");
1132 gtk_widget_show (label_format);
1133 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1134 (GtkAttachOptions) (GTK_FILL),
1135 (GtkAttachOptions) (0), 0, 0);
1138 channels_label = gtk_label_new ("Channels:");
1139 gtk_widget_show (channels_label);
1140 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1141 (GtkAttachOptions) (GTK_FILL),
1142 (GtkAttachOptions) (0), 0, 0);
1143 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1145 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1146 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1147 gtk_widget_show (forward_rb);
1148 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1149 (GtkAttachOptions) (GTK_FILL),
1150 (GtkAttachOptions) (0), 0, 0);
1152 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1153 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1154 gtk_widget_show (reversed_rb);
1155 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1156 (GtkAttachOptions) (GTK_FILL),
1157 (GtkAttachOptions) (0), 0, 0);
1159 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1160 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1161 gtk_widget_show (both_rb);
1162 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1163 (GtkAttachOptions) (GTK_FILL),
1164 (GtkAttachOptions) (0), 0, 0);
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1168 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1169 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1170 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1171 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1172 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1174 /* Connect the cancel_button to destroy the widget */
1175 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1176 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1177 user_data->dlg.save_csv_as_w);
1179 /* Catch the "key_press_event" signal in the window, so that we can catch
1180 the ESC key being pressed and act as if the "Cancel" button had
1182 dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1184 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
1185 user_data->dlg.save_csv_as_w);
1187 gtk_widget_show(user_data->dlg.save_csv_as_w);
1191 /****************************************************************************/
1192 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1194 /* Note that we no longer have a Save voice info dialog box. */
1195 user_data->dlg.save_voice_as_w = NULL;
1198 /****************************************************************************/
1199 /* here we save it into a file that user specified */
1200 /* XXX what about endians here? could go something wrong? */
1201 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1203 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1207 guint32 f_write_silence = 0;
1208 guint32 r_write_silence = 0;
1210 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1211 gboolean stop_flag = FALSE;
1213 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1216 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1222 /* open file for saving */
1223 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1230 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
1232 /* First we write the .au header. XXX Hope this is endian independant */
1233 /* the magic word 0x2e736e64 == .snd */
1234 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1235 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1236 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1237 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1238 /* header offset == 24 bytes */
1239 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1240 write(to_fd, pd, 1);
1241 write(to_fd, pd, 1);
1242 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1243 /* total length, it is permited to set this to 0xffffffff */
1244 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1245 write(to_fd, pd, 1);
1246 write(to_fd, pd, 1);
1247 write(to_fd, pd, 1);
1248 /* encoding format == 8 bit ulaw */
1249 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1250 write(to_fd, pd, 1);
1251 write(to_fd, pd, 1);
1252 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1253 /* sample rate == 8000 Hz */
1254 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1255 write(to_fd, pd, 1);
1256 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1257 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1259 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1260 write(to_fd, pd, 1);
1261 write(to_fd, pd, 1);
1262 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1265 /* only forward direction */
1267 progbar_count = user_data->forward.saveinfo.count;
1268 progbar_quantum = user_data->forward.saveinfo.count/100;
1269 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1272 if((count > progbar_nextstep) && (count <= progbar_count)) {
1273 update_progress_dlg(progbar,
1274 (gfloat) count/progbar_count, "Saving");
1275 progbar_nextstep = progbar_nextstep + progbar_quantum;
1278 *pd = (unsigned char)linear2ulaw(f_pd);
1279 fwritten = write(to_fd, pd, 1);
1280 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1284 destroy_progress_dlg(progbar);
1290 /* only reversed direction */
1292 progbar_count = user_data->reversed.saveinfo.count;
1293 progbar_quantum = user_data->reversed.saveinfo.count/100;
1294 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1297 if((count > progbar_nextstep) && (count <= progbar_count)) {
1298 update_progress_dlg(progbar,
1299 (gfloat) count/progbar_count, "Saving");
1300 progbar_nextstep = progbar_nextstep + progbar_quantum;
1303 *pd = (unsigned char)linear2ulaw(r_pd);
1304 rwritten = write(to_fd, pd, 1);
1305 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1309 destroy_progress_dlg(progbar);
1315 /* both directions */
1317 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
1318 (progbar_count = user_data->forward.saveinfo.count) :
1319 (progbar_count = user_data->reversed.saveinfo.count);
1320 progbar_quantum = progbar_count/100;
1321 /* since conversation in one way can start later than in the other one,
1322 * we have to write some silence information for one channel */
1323 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1324 f_write_silence = (guint32)
1325 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
1327 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1328 r_write_silence = (guint32)
1329 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
1334 if((count > progbar_nextstep) && (count <= progbar_count)) {
1335 update_progress_dlg(progbar,
1336 (gfloat) count/progbar_count, "Saving");
1337 progbar_nextstep = progbar_nextstep + progbar_quantum;
1340 if(f_write_silence > 0) {
1341 rread = read(rev_fd, &r_pd, 2);
1346 else if(r_write_silence > 0) {
1347 fread = read(forw_fd, &f_pd, 2);
1353 fread = read(forw_fd, &f_pd, 2);
1354 rread = read(rev_fd, &r_pd, 2);
1356 if ((rread == 0) && (fread == 0))
1358 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1359 rwritten = write(to_fd, pd, 1);
1360 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1364 destroy_progress_dlg(progbar);
1370 destroy_progress_dlg(progbar);
1378 /****************************************************************************/
1379 /* the user wants to save in a file */
1380 /* XXX support for different formats is currently commented out */
1381 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1384 /*GtkWidget *wav, *au, *sw;*/
1385 GtkWidget *rev, *forw, *both;
1386 user_data_t *user_data;
1387 gint channels /*, format*/;
1389 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1391 /* Perhaps the user specified a directory instead of a file.
1392 Check whether they did. */
1393 if (test_for_directory(g_dest) == EISDIR) {
1394 /* It's a directory - set the file selection box to display it. */
1395 set_last_open_dir(g_dest);
1397 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1401 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1402 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1403 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1404 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1405 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1406 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1407 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1409 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1410 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1411 * disable the ok button or disable the buttons for direction if only one is not ok. The
1412 * problem is if we open the save voice dialog and then click the refresh button and maybe
1413 * the state changes, so we can't save anymore. In this case we should be able to update
1414 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1417 /* we can not save in both dirctions */
1418 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1419 /* there are many combinations here, we just exit when first matches */
1420 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
1421 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1422 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1423 "Can't save in a file: Unsupported codec!");
1424 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
1425 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1426 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1427 "Can't save in a file: Wrong length of captured packets!");
1428 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
1429 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1430 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1431 "Can't save in a file: RTP data with padding!");
1433 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1434 "Can't save in a file: File I/O problem!");
1437 /* we can not save forward direction */
1438 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1439 (GTK_TOGGLE_BUTTON (both)->active))) {
1440 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1441 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1442 "Can't save forward direction in a file: Unsupported codec!");
1443 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1444 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1445 "Can't save forward direction in a file: Wrong length of captured packets!");
1446 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1447 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1448 "Can't save forward direction in a file: RTP data with padding!");
1450 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1451 "Can't save forward direction in a file: File I/O problem!");
1454 /* we can not save reversed direction */
1455 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1456 (GTK_TOGGLE_BUTTON (both)->active))) {
1457 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1458 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1459 "Can't save reversed direction in a file: Unsupported codec!");
1460 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1461 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1462 "Can't save reversed direction in a file: Wrong length of captured packets!");
1463 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1464 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1465 "Can't save reversed direction in a file: RTP data with padding!");
1466 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1467 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1468 "Can't save reversed direction in a file: No RTP data!");
1470 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1471 "Can't save reversed direction in a file: File I/O problem!");
1475 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1477 else if (GTK_TOGGLE_BUTTON (au)->active)
1479 else if (GTK_TOGGLE_BUTTON (sw)->active)
1482 if (GTK_TOGGLE_BUTTON (rev)->active)
1484 else if (GTK_TOGGLE_BUTTON (both)->active)
1489 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1490 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1491 "An error occured while saving voice in a file!");
1495 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1498 /****************************************************************************/
1499 /* when the user wants to save the voice information in a file */
1500 /* XXX support for different formats is currently commented out */
1501 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1505 GtkWidget *label_format;
1506 GtkWidget *channels_label;
1507 /*GSList *format_group = NULL;*/
1508 GSList *channels_group = NULL;
1509 GtkWidget *forward_rb;
1510 GtkWidget *reversed_rb;
1512 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1515 /* if we can't save in a file: wrong codec, cut packets or other errors */
1516 /* shold the error arise here or later when you click ok button ?
1517 * if we do it here, then we must disable the refresh button, so we don't do it here */
1519 if (user_data->dlg.save_voice_as_w != NULL) {
1520 /* There's already a Save voice info dialog box; reactivate it. */
1521 reactivate_window(user_data->dlg.save_voice_as_w);
1525 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1526 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
1527 save_voice_as_destroy_cb, user_data);
1529 /* Container for each row of widgets */
1530 vertb = gtk_vbox_new(FALSE, 0);
1531 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1532 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1533 vertb, FALSE, FALSE, 0);
1534 gtk_widget_show (vertb);
1536 table1 = gtk_table_new (2, 4, FALSE);
1537 gtk_widget_show (table1);
1538 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1539 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1540 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1542 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1543 gtk_widget_show (label_format);
1544 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1545 (GtkAttachOptions) (GTK_FILL),
1546 (GtkAttachOptions) (0), 0, 0);
1548 /* we support .au - ulaw*/
1549 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1550 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1551 gtk_widget_show (wav_rb);
1552 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1553 (GtkAttachOptions) (GTK_FILL),
1554 (GtkAttachOptions) (0), 0, 0);
1556 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1557 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1558 gtk_widget_show (sw_rb);
1559 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1560 (GtkAttachOptions) (GTK_FILL),
1561 (GtkAttachOptions) (0), 0, 0);
1562 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1563 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1564 gtk_widget_show (au_rb);
1565 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1566 (GtkAttachOptions) (GTK_FILL),
1567 (GtkAttachOptions) (0), 0, 0);
1570 channels_label = gtk_label_new ("Channels:");
1571 gtk_widget_show (channels_label);
1572 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1573 (GtkAttachOptions) (GTK_FILL),
1574 (GtkAttachOptions) (0), 0, 0);
1575 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1577 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1578 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1579 gtk_widget_show (forward_rb);
1580 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1581 (GtkAttachOptions) (GTK_FILL),
1582 (GtkAttachOptions) (0), 0, 0);
1584 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1585 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1586 gtk_widget_show (reversed_rb);
1587 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1588 (GtkAttachOptions) (GTK_FILL),
1589 (GtkAttachOptions) (0), 0, 0);
1591 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1592 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1593 gtk_widget_show (both_rb);
1594 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1595 (GtkAttachOptions) (GTK_FILL),
1596 (GtkAttachOptions) (0), 0, 0);
1598 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1600 /* if one direction is nok we don't allow saving
1601 XXX this is not ok since the user can click the refresh button and cause changes
1602 but we can not update this window. So we move all the decision on the time the ok
1604 if (user_data->forward.saved == FALSE) {
1605 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1606 gtk_widget_set_sensitive(forward_rb, FALSE);
1607 gtk_widget_set_sensitive(both_rb, FALSE);
1609 else if (user_data->reversed.saved == FALSE) {
1610 gtk_widget_set_sensitive(reversed_rb, FALSE);
1611 gtk_widget_set_sensitive(both_rb, FALSE);
1615 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1616 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1617 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1618 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1619 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1620 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1621 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1622 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1624 /* Connect the cancel_button to destroy the widget */
1625 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1626 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1627 user_data->dlg.save_voice_as_w);
1629 /* Catch the "key_press_event" signal in the window, so that we can catch
1630 the ESC key being pressed and act as if the "Cancel" button had
1632 dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1634 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
1635 user_data->dlg.save_voice_as_w);
1637 gtk_widget_show(user_data->dlg.save_voice_as_w);
1641 /****************************************************************************/
1642 /* when we are finished with redisection, we add the label for the statistic */
1643 static void draw_stat(user_data_t *user_data)
1645 gchar label_max[200];
1646 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1647 - user_data->forward.statinfo.start_seq_nr + 1;
1648 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1649 - user_data->reversed.statinfo.start_seq_nr + 1;
1650 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1651 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1653 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1654 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1655 " Sequence errors = %u",
1656 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr,
1657 user_data->forward.statinfo.total_nr,
1658 f_expected, f_lost, user_data->forward.statinfo.sequence);
1660 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1662 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1663 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1664 " Sequence errors = %u",
1665 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr,
1666 user_data->reversed.statinfo.total_nr,
1667 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1669 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1677 /****************************************************************************/
1678 /* append a line to clist */
1679 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1680 double delay, double jitter, gchar *status, gboolean marker,
1681 gchar *timeStr, guint32 pkt_len, GdkColor *color)
1687 data[0]=&field[0][0];
1688 data[1]=&field[1][0];
1689 data[2]=&field[2][0];
1690 data[3]=&field[3][0];
1691 data[4]=&field[4][0];
1692 data[5]=&field[5][0];
1693 data[6]=&field[6][0];
1694 data[7]=&field[7][0];
1696 g_snprintf(field[0], 20, "%u", number);
1697 g_snprintf(field[1], 20, "%u", seq_num);
1698 g_snprintf(field[2], 20, "%f", delay);
1699 g_snprintf(field[3], 20, "%f", jitter);
1700 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1701 g_snprintf(field[5], 40, "%s", status);
1702 g_snprintf(field[6], 32, "%s", timeStr);
1703 g_snprintf(field[7], 20, "%u", pkt_len);
1705 added_row = gtk_clist_append(GTK_CLIST(clist), data);
1706 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1707 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1711 /****************************************************************************/
1712 /* callback for sorting columns of clist */
1713 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
1720 const GtkCListRow *row1 = (GtkCListRow *) ptr1;
1721 const GtkCListRow *row2 = (GtkCListRow *) ptr2;
1723 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
1724 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
1726 switch(clist->sort_column){
1727 /* columns representing strings */
1731 return strcmp (text1, text2);
1732 /* columns representing ints */
1739 /* columns representing floats */
1744 if (fabs(f1-f2)<0.0000005)
1750 g_assert_not_reached();
1755 /****************************************************************************/
1757 click_column_cb(GtkCList *clist, gint column, gpointer data)
1759 column_arrows *col_arrows = (column_arrows *) data;
1762 gtk_clist_freeze(clist);
1764 for (i = 0; i < NUM_COLS; i++) {
1765 gtk_widget_hide(col_arrows[i].ascend_pm);
1766 gtk_widget_hide(col_arrows[i].descend_pm);
1769 if (column == clist->sort_column) {
1770 if (clist->sort_type == GTK_SORT_ASCENDING) {
1771 clist->sort_type = GTK_SORT_DESCENDING;
1772 gtk_widget_show(col_arrows[column].descend_pm);
1774 clist->sort_type = GTK_SORT_ASCENDING;
1775 gtk_widget_show(col_arrows[column].ascend_pm);
1778 clist->sort_type = GTK_SORT_ASCENDING;
1779 gtk_widget_show(col_arrows[column].ascend_pm);
1780 gtk_clist_set_sort_column(clist, column);
1782 gtk_clist_thaw(clist);
1784 gtk_clist_sort(clist);
1788 /****************************************************************************/
1789 /* Add the packet list */
1791 GtkWidget* create_clist(user_data_t* user_data)
1793 GtkWidget* clist_fwd;
1795 /* clist for the information */
1796 clist_fwd = gtk_clist_new(8);
1797 gtk_widget_show(clist_fwd);
1798 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
1800 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
1801 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
1802 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
1803 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
1805 /* hide date and length column */
1806 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1807 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1809 /* column widths and justification */
1810 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
1811 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
1812 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
1813 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
1814 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
1815 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
1816 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
1817 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1818 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1819 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1820 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1826 /****************************************************************************/
1827 /* Add the sort by column feature for a packet clist */
1829 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
1830 user_data_t* user_data _U_)
1832 column_arrows *col_arrows;
1833 GdkBitmap *ascend_bm, *descend_bm;
1834 GdkPixmap *ascend_pm, *descend_pm;
1835 GtkStyle *win_style;
1836 GtkWidget *column_lb;
1839 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
1840 win_style = gtk_widget_get_style(window);
1841 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1843 &win_style->bg[GTK_STATE_NORMAL],
1844 (gchar **)clist_ascend_xpm);
1845 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1847 &win_style->bg[GTK_STATE_NORMAL],
1848 (gchar **)clist_descend_xpm);
1850 for (i=0; i<NUM_COLS; i++) {
1851 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1852 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1853 column_lb = gtk_label_new(titles[i]);
1854 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1855 gtk_widget_show(column_lb);
1857 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1858 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1859 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1860 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1861 /* make packet-nr be the default sort order */
1863 gtk_widget_show(col_arrows[i].ascend_pm);
1865 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
1866 gtk_widget_show(col_arrows[i].table);
1869 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
1874 /****************************************************************************/
1875 /* Create the dialog box with all widgets */
1876 void create_rtp_dialog(user_data_t* user_data)
1878 GtkWidget *window = NULL;
1879 GtkWidget *clist_fwd;
1880 GtkWidget *clist_rev;
1881 GtkWidget *label_stats_fwd;
1882 GtkWidget *label_stats_rev;
1883 GtkWidget *notebook;
1885 GtkWidget *main_vb, *page, *page_r;
1887 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1888 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1889 #ifdef USE_CONVERSATION_GRAPH
1890 GtkWidget *graph_bt;
1893 gchar label_forward[150];
1894 gchar label_reverse[150];
1896 gchar str_ip_src[16];
1897 gchar str_ip_dst[16];
1898 column_arrows *col_arrows_fwd;
1899 column_arrows *col_arrows_rev;
1902 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1903 gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1904 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1905 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
1907 /* Container for each row of widgets */
1908 main_vb = gtk_vbox_new(FALSE, 2);
1909 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
1910 gtk_container_add(GTK_CONTAINER(window), main_vb);
1911 gtk_widget_show(main_vb);
1914 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1915 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1917 g_snprintf(label_forward, 149,
1918 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1919 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1921 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1922 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1924 g_snprintf(label_reverse, 149,
1925 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1926 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1928 /* Start a notebook for flipping between sets of changes */
1929 notebook = gtk_notebook_new();
1930 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1931 OBJECT_SET_DATA(window, "notebook", notebook);
1932 SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
1935 /* page for forward connection */
1936 page = gtk_vbox_new(FALSE, 8);
1937 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1939 /* direction label */
1940 label = gtk_label_new(label_forward);
1941 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
1943 /* place for some statistics */
1944 label_stats_fwd = gtk_label_new("\n");
1945 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
1947 /* scrolled window */
1948 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1949 WIDGET_SET_SIZE(scrolled_window, 560, 200);
1950 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1951 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1954 clist_fwd = create_clist(user_data);
1955 gtk_widget_show(clist_fwd);
1956 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1957 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1958 gtk_widget_show(scrolled_window);
1961 label = gtk_label_new(" Forward Direction ");
1962 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1964 /* same page for reversed connection */
1965 page_r = gtk_vbox_new(FALSE, 8);
1966 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
1967 label = gtk_label_new(label_reverse);
1968 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
1969 label_stats_rev = gtk_label_new("\n");
1970 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
1972 scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
1973 WIDGET_SET_SIZE(scrolled_window_r, 560, 200);
1974 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
1975 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1977 clist_rev = create_clist(user_data);
1978 gtk_widget_show(clist_rev);
1979 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
1980 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
1981 gtk_widget_show(scrolled_window_r);
1983 label = gtk_label_new(" Reversed Direction ");
1984 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
1986 /* page for help&about or future
1987 page_help = gtk_hbox_new(FALSE, 5);
1988 label = gtk_label_new(" Future ");
1989 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
1990 frame = gtk_frame_new("");
1991 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
1992 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
1993 gtk_container_add(GTK_CONTAINER(frame), text);
1994 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
1995 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
1998 /* show all notebooks */
1999 gtk_widget_show_all(notebook);
2002 box4 = gtk_hbutton_box_new();
2003 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
2004 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
2005 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
2006 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
2007 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
2008 gtk_widget_show(box4);
2010 voice_bt = gtk_button_new_with_label("Save payload...");
2011 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
2012 gtk_widget_show(voice_bt);
2013 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
2015 csv_bt = gtk_button_new_with_label("Save as CSV...");
2016 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
2017 gtk_widget_show(csv_bt);
2018 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
2020 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
2021 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
2022 gtk_widget_show(refresh_bt);
2023 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
2025 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
2026 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
2027 gtk_widget_show(goto_bt);
2028 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
2030 #ifdef USE_CONVERSATION_GRAPH
2031 graph_bt = gtk_button_new_with_label("Graph");
2032 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
2033 gtk_widget_show(graph_bt);
2034 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
2037 next_bt = gtk_button_new_with_label("Next non-Ok");
2038 gtk_container_add(GTK_CONTAINER(box4), next_bt);
2039 gtk_widget_show(next_bt);
2040 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
2042 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
2043 gtk_container_add(GTK_CONTAINER(box4), close_bt);
2044 gtk_widget_show(close_bt);
2045 SIGNAL_CONNECT(close_bt, "clicked", on_close_bt_clicked, user_data);
2047 gtk_widget_show(window);
2049 /* sort by column feature */
2050 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
2051 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
2053 /* some widget references need to be saved for outside use */
2054 user_data->dlg.window = window;
2055 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
2056 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
2057 user_data->dlg.label_stats_fwd = label_stats_fwd;
2058 user_data->dlg.label_stats_rev = label_stats_rev;
2059 user_data->dlg.notebook = notebook;
2060 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
2061 user_data->dlg.selected_row = 0;
2062 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
2063 user_data->dlg.col_arrows_rev = col_arrows_rev;
2067 /****************************************************************************/
2068 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
2069 gchar* proto_field, guint32* p_result)
2072 proto_node *proto_sibling_node;
2073 header_field_info *hfssrc;
2076 finfo = PITEM_FINFO(ptree_node);
2078 if (hfinformation==(finfo->hfinfo)) {
2079 hfssrc = proto_registrar_get_byname(proto_field);
2082 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
2083 ptree_node=ptree_node->next) {
2084 finfo=PITEM_FINFO(ptree_node);
2085 if (hfssrc==finfo->hfinfo) {
2086 if (hfinformation->type==FT_IPv4) {
2087 ipv4 = fvalue_get(&finfo->value);
2088 *p_result = ipv4_get_net_order_addr(ipv4);
2091 *p_result = fvalue_get_integer(&finfo->value);
2098 proto_sibling_node = ptree_node->next;
2100 if (proto_sibling_node) {
2101 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
2107 /****************************************************************************/
2108 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
2113 proto_node *ptree_node;
2114 header_field_info *hfinformation;
2116 hfinformation = proto_registrar_get_byname(proto_name);
2117 if (hfinformation == NULL)
2120 ptree_node = ((proto_node *)protocol_tree)->first_child;
2124 return process_node(ptree_node, hfinformation, proto_field, p_result);
2128 /* XXX just copied from gtk/rpc_stat.c */
2129 void protect_thread_critical_region(void);
2130 void unprotect_thread_critical_region(void);
2132 /****************************************************************************/
2133 /* XXX only handles RTP over IPv4, should add IPv6 support */
2136 guint16 port_src_fwd,
2138 guint16 port_dst_fwd,
2141 guint16 port_src_rev,
2143 guint16 port_dst_rev,
2147 user_data_t *user_data;
2151 user_data = g_malloc(sizeof(user_data_t));
2153 user_data->ip_src_fwd = ip_src_fwd;
2154 user_data->port_src_fwd = port_src_fwd;
2155 user_data->ip_dst_fwd = ip_dst_fwd;
2156 user_data->port_dst_fwd = port_dst_fwd;
2157 user_data->ssrc_fwd = ssrc_fwd;
2158 user_data->ip_src_rev = ip_src_rev;
2159 user_data->port_src_rev = port_src_rev;
2160 user_data->ip_dst_rev = ip_dst_rev;
2161 user_data->port_dst_rev = port_dst_rev;
2162 user_data->ssrc_rev = ssrc_rev;
2164 /* file names for storing sound data */
2165 /*XXX: check for errors*/
2166 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
2169 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
2172 user_data->forward.saveinfo.fp = NULL;
2173 user_data->reversed.saveinfo.fp = NULL;
2174 user_data->dlg.save_voice_as_w = NULL;
2175 user_data->dlg.save_csv_as_w = NULL;
2176 #ifdef USE_CONVERSATION_GRAPH
2177 user_data->dlg.graph_window = NULL;
2178 user_data->series_fwd.value_pairs = NULL;
2179 user_data->series_rev.value_pairs = NULL;
2182 /* create the dialog box */
2183 create_rtp_dialog(user_data);
2185 /* proceed as if the Refresh button would have been pressed */
2186 on_refresh_bt_clicked(NULL, user_data);
2189 /****************************************************************************/
2190 /* entry point from main menu */
2191 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
2194 guint16 port_src_fwd;
2196 guint16 port_dst_fwd;
2197 guint32 ssrc_fwd = 0;
2199 guint16 port_src_rev;
2201 guint16 port_dst_rev;
2202 guint32 ssrc_rev = 0;
2204 gchar filter_text[256];
2207 epan_dissect_t *edt;
2210 gboolean frame_matched;
2212 GList *strinfo_list;
2213 GList *filtered_list = NULL;
2214 rtp_stream_info_t *strinfo;
2217 /* Try to compile the filter. */
2218 strcpy(filter_text,"rtp && ip");
2219 if (!dfilter_compile(filter_text, &sfcode)) {
2220 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
2223 /* we load the current file into cf variable */
2225 fdata = cf->current_frame;
2227 /* we are on the selected frame now */
2229 return; /* if we exit here it's an error */
2231 /* dissect the current frame */
2232 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
2233 cf->pd, fdata->cap_len, &err, &err_info)) {
2234 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
2235 cf_read_error_message(err, err_info), cf->filename);
2238 edt = epan_dissect_new(TRUE, FALSE);
2239 epan_dissect_prime_dfilter(edt, sfcode);
2240 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
2241 frame_matched = dfilter_apply_edt(sfcode, edt);
2243 /* if it is not an rtp frame, show the rtpstream dialog */
2244 frame_matched = dfilter_apply_edt(sfcode, edt);
2245 if (frame_matched != 1) {
2246 epan_dissect_free(edt);
2247 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!");
2251 /* ok, it is a RTP frame, so let's get the ip and port values */
2252 g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
2253 g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
2254 port_src_fwd = edt->pi.srcport;
2255 port_dst_fwd = edt->pi.destport;
2257 /* assume the inverse ip/port combination for the reverse direction */
2258 g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
2259 g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
2260 port_src_rev = edt->pi.destport;
2261 port_dst_rev = edt->pi.srcport;
2263 /* now we need the SSRC value of the current frame */
2264 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
2265 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!");
2269 /* search for reversed direction in the global rtp streams list */
2271 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
2272 while (strinfo_list)
2274 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
2275 if (strinfo->src_addr==ip_src_fwd
2276 && strinfo->src_port==port_src_fwd
2277 && strinfo->dest_addr==ip_dst_fwd
2278 && strinfo->dest_port==port_dst_fwd)
2280 filtered_list = g_list_prepend(filtered_list, strinfo);
2283 if (strinfo->src_addr==ip_src_rev
2284 && strinfo->src_port==port_src_rev
2285 && strinfo->dest_addr==ip_dst_rev
2286 && strinfo->dest_port==port_dst_rev)
2289 filtered_list = g_list_append(filtered_list, strinfo);
2291 ssrc_rev = strinfo->ssrc;
2294 strinfo_list = g_list_next(strinfo_list);
2297 /* if more than one reverse streams found, we let the user choose the right one */
2299 rtpstream_dlg_show(filtered_list);
2318 /****************************************************************************/
2320 rtp_analysis_init(char *dummy _U_)
2322 rtp_analysis_cb(NULL, NULL);
2325 /****************************************************************************/
2327 register_tap_listener_rtp_analysis(void)
2329 register_ethereal_tap("rtp", rtp_analysis_init);
2333 register_tap_menu_rtp_analysis(void)
2335 register_tap_menu_item("_Statistics/RTP Streams/Analyse...",
2336 rtp_analysis_cb, NULL, NULL, NULL);