2 * RTP analysis addition for ethereal
4 * $Id: rtp_analysis.c,v 1.16 2003/12/28 12:43:40 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"
47 #include "epan/epan_dissect.h"
48 #include "epan/filesystem.h"
51 #include "packet-rtp.h"
60 #include "dlg_utils.h"
62 #include "simple_dialog.h"
65 #include "progress_dlg.h"
66 #include "compat_macros.h"
77 #include <io.h> /* open/close on win32 */
84 /****************************************************************************/
86 typedef struct _dialog_data_t {
90 GtkWidget *label_stats_fwd;
91 GtkWidget *label_stats_rev;
93 GtkCList *selected_clist;
94 GtkWidget *save_voice_as_w;
95 GtkWidget *save_csv_as_w;
97 #ifdef USE_CONVERSATION_GRAPH
98 GtkWidget *graph_window;
104 /* type of error when saving voice in a file didn't succeed */
107 TAP_RTP_WRONG_LENGTH,
108 TAP_RTP_PADDING_ERROR,
109 TAP_RTP_FILE_OPEN_ERROR,
114 /****************************************************************************/
115 /* structure that holds the information about the forward and reversed direction */
116 typedef struct _tap_rtp_stat_t {
117 gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
118 /* use (flags & STAT_FLAG_FIRST) instead */
119 /* all of the following fields will be initialized after
120 rtp_packet_analyse has been called */
121 guint32 flags; /* see STAT_FLAG-defines below */
124 guint32 delta_timestamp;
131 guint16 start_seq_nr;
140 /* status flags for the flags parameter in tap_rtp_stat_t */
141 #define STAT_FLAG_FIRST 0x01
142 #define STAT_FLAG_MARKER 0x02
143 #define STAT_FLAG_WRONG_SEQ 0x04
144 #define STAT_FLAG_PT_CHANGE 0x08
145 #define STAT_FLAG_PT_CN 0x10
147 typedef struct _tap_rtp_save_info_t {
150 error_type_t error_type;
152 } tap_rtp_save_info_t;
155 /* structure that holds the information about the forward and reversed direction */
156 struct _info_direction {
157 tap_rtp_stat_t statinfo;
158 tap_rtp_save_info_t saveinfo;
161 #define TMPNAMSIZE 100
163 /* structure that holds general information about the connection
164 * and structures for both directions */
165 typedef struct _user_data_t {
166 /* tap associated data*/
168 guint16 port_src_fwd;
170 guint16 port_dst_fwd;
173 guint16 port_src_rev;
175 guint16 port_dst_rev;
178 struct _info_direction forward;
179 struct _info_direction reversed;
181 char f_tempname[TMPNAMSIZE];
182 char r_tempname[TMPNAMSIZE];
184 /* dialog associated data */
187 #ifdef USE_CONVERSATION_GRAPH
188 time_series_t series_fwd;
189 time_series_t series_rev;
194 typedef const guint8 * ip_addr_p;
197 /****************************************************************************/
200 /****************************************************************************/
201 /* when there is a [re]reading of packet's */
203 rtp_reset(user_data_t *user_data _U_)
205 user_data->forward.statinfo.first_packet = TRUE;
206 user_data->reversed.statinfo.first_packet = TRUE;
207 user_data->forward.statinfo.max_delay = 0;
208 user_data->reversed.statinfo.max_delay = 0;
209 user_data->forward.statinfo.delay = 0;
210 user_data->reversed.statinfo.delay = 0;
211 user_data->forward.statinfo.jitter = 0;
212 user_data->reversed.statinfo.jitter = 0;
213 user_data->forward.statinfo.timestamp = 0;
214 user_data->reversed.statinfo.timestamp = 0;
215 user_data->forward.statinfo.max_nr = 0;
216 user_data->reversed.statinfo.max_nr = 0;
217 user_data->forward.statinfo.total_nr = 0;
218 user_data->reversed.statinfo.total_nr = 0;
219 user_data->forward.statinfo.sequence = 0;
220 user_data->reversed.statinfo.sequence = 0;
221 user_data->forward.statinfo.start_seq_nr = 0;
222 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
223 user_data->forward.statinfo.stop_seq_nr = 0;
224 user_data->reversed.statinfo.stop_seq_nr = 0;
225 user_data->forward.statinfo.cycles = 0;
226 user_data->reversed.statinfo.cycles = 0;
227 user_data->forward.statinfo.under = FALSE;
228 user_data->reversed.statinfo.under = FALSE;
229 user_data->forward.statinfo.start_time = 0;
230 user_data->reversed.statinfo.start_time = 0;
231 user_data->forward.statinfo.time = 0;
232 user_data->reversed.statinfo.time = 0;
234 user_data->forward.saveinfo.count = 0;
235 user_data->reversed.saveinfo.count = 0;
236 user_data->forward.saveinfo.saved = FALSE;
237 user_data->reversed.saveinfo.saved = FALSE;
239 #ifdef USE_CONVERSATION_GRAPH
240 if (user_data->dlg.graph_window != NULL)
241 gtk_widget_destroy(user_data->dlg.graph_window);
243 g_array_free(user_data->series_fwd.value_pairs, TRUE);
244 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
246 g_array_free(user_data->series_rev.value_pairs, TRUE);
247 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
250 /* XXX check for error at fclose? */
251 if (user_data->forward.saveinfo.fp != NULL)
252 fclose(user_data->forward.saveinfo.fp);
253 if (user_data->reversed.saveinfo.fp != NULL)
254 fclose(user_data->reversed.saveinfo.fp);
255 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
256 if (user_data->forward.saveinfo.fp == NULL)
257 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
258 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
259 if (user_data->reversed.saveinfo.fp == NULL)
260 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
264 /****************************************************************************/
265 /* here we can redraw the output */
267 static void rtp_draw(void *prs _U_)
272 /* forward declarations */
273 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
274 double delay, double jitter, gchar *status, gboolean marker,
275 gchar *timeStr, guint32 pkt_len, GdkColor *color);
277 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
278 packet_info *pinfo, struct _rtp_info *rtpinfo);
279 static int rtp_packet_add_info(GtkCList *clist,
280 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
281 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
282 tap_rtp_stat_t *statinfo,
283 packet_info *pinfo, struct _rtp_info *rtpinfo);
286 /****************************************************************************/
287 /* whenever a RTP packet is seen by the tap listener */
288 static int rtp_packet(user_data_t *user_data, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo)
290 #ifdef USE_CONVERSATION_GRAPH
294 /* we ignore packets that are not displayed */
295 if (pinfo->fd->flags.passed_dfilter == 0)
298 /* is it the forward direction? */
299 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
300 #ifdef USE_CONVERSATION_GRAPH
301 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
302 vp.fnumber = pinfo->fd->num;
303 g_array_append_val(user_data->series_fwd.value_pairs, vp);
305 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
306 rtp_packet_add_info(user_data->dlg.clist_fwd,
307 &(user_data->forward.statinfo), pinfo, rtpinfo);
308 rtp_packet_save_payload(&(user_data->forward.saveinfo),
309 &(user_data->forward.statinfo), pinfo, rtpinfo);
311 /* is it the reversed direction? */
312 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
313 #ifdef USE_CONVERSATION_GRAPH
314 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
315 vp.fnumber = pinfo->fd->num;
316 g_array_append_val(user_data->series_rev.value_pairs, vp);
318 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
319 rtp_packet_add_info(user_data->dlg.clist_rev,
320 &(user_data->reversed.statinfo), pinfo, rtpinfo);
321 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
322 &(user_data->reversed.statinfo), pinfo, rtpinfo);
329 /****************************************************************************/
330 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
331 packet_info *pinfo, struct _rtp_info *rtpinfo)
334 double current_jitter;
338 /* check payload type */
339 if (rtpinfo->info_payload_type == PT_CN
340 || rtpinfo->info_payload_type == PT_CN_OLD)
341 statinfo->flags |= STAT_FLAG_PT_CN;
342 if (rtpinfo->info_payload_type != statinfo->pt)
343 statinfo->flags |= STAT_FLAG_PT_CHANGE;
345 statinfo->pt = rtpinfo->info_payload_type;
347 /* store the current time and calculate the current jitter */
348 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
349 current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
350 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
351 statinfo->delay = current_time-(statinfo->time);
352 statinfo->jitter = current_jitter;
354 /* is this the first packet we got in this direction? */
355 if (statinfo->first_packet) {
356 statinfo->start_seq_nr = rtpinfo->info_seq_num;
357 statinfo->start_time = current_time;
359 statinfo->jitter = 0;
360 statinfo->flags |= STAT_FLAG_FIRST;
361 statinfo->first_packet = FALSE;
363 /* is it a packet with the mark bit set? */
364 if (rtpinfo->info_marker_set) {
365 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
366 statinfo->flags |= STAT_FLAG_MARKER;
368 /* if neither then it is a normal packet */
369 if (!(statinfo->first_packet) && !(rtpinfo->info_marker_set)) {
370 if (statinfo->delay > statinfo->max_delay) {
371 statinfo->max_delay = statinfo->delay;
372 statinfo->max_nr = pinfo->fd->num;
376 /* When calculating expected rtp packets the seq number can wrap around
377 * so we have to count the number of cycles
378 * Variable cycles counts the wraps around in forwarding connection and
379 * under is flag that indicates where we are
381 * XXX how to determine number of cycles with all possible lost, late
382 * and duplicated packets without any doubt? It seems to me, that
383 * because of all possible combination of late, duplicated or lost
384 * packets, this can only be more or less good approximation
386 * There are some combinations (rare but theoretically possible),
387 * where below code won't work correctly - statistic may be wrong then.
390 /* so if the current sequence number is less than the start one
391 * we assume, that there is another cycle running */
392 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
394 statinfo->under = TRUE;
396 /* what if the start seq nr was 0? Then the above condition will never
397 * be true, so we add another condition. XXX The problem would arise
398 * if one of the packets with seq nr 0 or 65535 would be lost or late */
399 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
400 (statinfo->under == FALSE)){
402 statinfo->under = TRUE;
404 /* the whole round is over, so reset the flag */
405 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
406 statinfo->under = FALSE;
409 /* Since it is difficult to count lost, duplicate or late packets separately,
410 * we would like to know at least how many times the sequence number was not ok */
412 /* if the current seq number equals the last one or if we are here for
413 * the first time, then it is ok, we just store the current one as the last one */
414 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
415 statinfo->seq_num = rtpinfo->info_seq_num;
416 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
417 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
418 statinfo->seq_num = rtpinfo->info_seq_num;
420 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
421 statinfo->seq_num = rtpinfo->info_seq_num;
422 statinfo->sequence++;
423 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
425 /* late or duplicated */
426 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
427 statinfo->sequence++;
428 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
431 statinfo->time = current_time;
432 statinfo->timestamp = rtpinfo->info_timestamp;
433 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
434 statinfo->total_nr++;
440 /****************************************************************************/
441 /* adds statistics information from the packet to the clist */
442 static int rtp_packet_add_info(GtkCList *clist,
443 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
450 GdkColor color = {0, 0xffff, 0xffff, 0xffff};
452 then = pinfo->fd->abs_secs;
453 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
454 tm_tmp = localtime(&then);
455 snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
458 tm_tmp->tm_year + 1900,
464 if (statinfo->pt == PT_CN) {
465 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
468 color.green = 0x7fff;
471 else if (statinfo->pt == PT_CN_OLD) {
472 snprintf(status,40,"Comfort noise (PT=19, reserved)");
475 color.green = 0x7fff;
478 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
479 snprintf(status,40,"Wrong sequence nr.");
482 color.green = 0x7fff;
485 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
486 && !(statinfo->flags & STAT_FLAG_FIRST)
487 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
488 snprintf(status,40,"Payload type changed to PT=%u", statinfo->pt);
491 color.green = 0x7fff;
495 snprintf(status,40,OK_TEXT);
498 /* is this the first packet we got in this direction? */
499 if (statinfo->flags & STAT_FLAG_FIRST) {
501 pinfo->fd->num, rtpinfo->info_seq_num,
505 rtpinfo->info_marker_set,
506 timeStr, pinfo->fd->pkt_len,
511 pinfo->fd->num, rtpinfo->info_seq_num,
515 rtpinfo->info_marker_set,
516 timeStr, pinfo->fd->pkt_len,
524 /****************************************************************************/
525 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
526 tap_rtp_stat_t *statinfo,
527 packet_info *pinfo, struct _rtp_info *rtpinfo)
533 /* is this the first packet we got in this direction? */
534 if (statinfo->flags & STAT_FLAG_FIRST) {
535 if (saveinfo->fp == NULL) {
536 saveinfo->saved = FALSE;
537 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
540 saveinfo->saved = TRUE;
543 /* save the voice information */
544 /* if there was already an error, we quit */
545 if (saveinfo->saved == FALSE)
548 /* if the captured length and packet length aren't equal, we quit
549 * because there is some information missing */
550 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
551 saveinfo->saved = FALSE;
552 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
556 /* if padding bit is set, but the padding count is bigger
557 * then the whole RTP data - error with padding count */
558 if ( (rtpinfo->info_padding_set != FALSE) &&
559 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
560 saveinfo->saved = FALSE;
561 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
565 /* do we need to insert some silence? */
566 if ((rtpinfo->info_marker_set) &&
567 !(statinfo->flags & STAT_FLAG_FIRST) &&
568 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
569 /* the amount of silence should be the difference between
570 * the last timestamp and the current one minus x
571 * x should equal the amount of information in the last frame
572 * XXX not done yet */
573 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
574 rtpinfo->info_padding_count); i++) {
575 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
576 fwrite(&tmp, 2, 1, saveinfo->fp);
579 fflush(saveinfo->fp);
583 if (rtpinfo->info_payload_type == PT_PCMU) {
584 /* we put the pointer at the beggining of the RTP data, that is
585 * at the end of the current frame minus the length of the
586 * padding count minus length of the RTP data */
587 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
588 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
589 tmp = (gint16 )ulaw2linear((unsigned char)*data);
590 fwrite(&tmp, 2, 1, saveinfo->fp);
593 fflush(saveinfo->fp);
594 saveinfo->saved = TRUE;
599 else if (rtpinfo->info_payload_type == PT_PCMA) {
600 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
601 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
602 tmp = (gint16 )alaw2linear((unsigned char)*data);
603 fwrite(&tmp, 2, 1, saveinfo->fp);
606 fflush(saveinfo->fp);
607 saveinfo->saved = TRUE;
610 /* comfort noise? - do nothing */
611 else if (rtpinfo->info_payload_type == PT_CN
612 || rtpinfo->info_payload_type == PT_CN_OLD) {
614 /* unsupported codec or XXX other error */
616 saveinfo->saved = FALSE;
617 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
625 /****************************************************************************/
628 /****************************************************************************/
629 /* XXX just copied from gtk/rpc_stat.c */
630 void protect_thread_critical_region(void);
631 void unprotect_thread_critical_region(void);
634 /****************************************************************************/
635 /* close the dialog window and remove the tap listener */
636 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
638 protect_thread_critical_region();
639 remove_tap_listener(user_data);
640 unprotect_thread_critical_region();
642 if (user_data->forward.saveinfo.fp != NULL)
643 fclose(user_data->forward.saveinfo.fp);
644 if (user_data->reversed.saveinfo.fp != NULL)
645 fclose(user_data->reversed.saveinfo.fp);
646 remove(user_data->f_tempname);
647 remove(user_data->r_tempname);
649 /* Is there a save voice window open? */
650 if (user_data->dlg.save_voice_as_w != NULL)
651 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
653 #ifdef USE_CONVERSATION_GRAPH
654 /* Is there a graph window open? */
655 if (user_data->dlg.graph_window != NULL)
656 gtk_widget_destroy(user_data->dlg.graph_window);
663 /****************************************************************************/
664 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
665 GtkNotebookPage *page _U_,
667 user_data_t *user_data _U_)
669 user_data->dlg.selected_clist =
670 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
671 user_data->dlg.selected_row = 0;
674 /****************************************************************************/
675 static void on_clist_select_row(GtkCList *clist _U_,
679 user_data_t *user_data _U_)
681 user_data->dlg.selected_clist = clist;
682 user_data->dlg.selected_row = row;
686 #ifdef USE_CONVERSATION_GRAPH
687 /****************************************************************************/
688 /* when the graph window gets destroyed */
689 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
691 /* note that graph window has been destroyed */
692 user_data->dlg.graph_window = NULL;
695 /****************************************************************************/
696 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
699 GtkCList *clist = NULL;
700 if (vp.fnumber != 0) {
701 clist = GTK_CLIST(user_data->dlg.clist_fwd);
702 row = gtk_clist_find_row_from_data(clist,
703 GUINT_TO_POINTER(vp.fnumber));
705 clist = GTK_CLIST(user_data->dlg.clist_rev);
706 row = gtk_clist_find_row_from_data(clist,
707 GUINT_TO_POINTER(vp.fnumber));
710 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
711 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
712 gtk_clist_select_row(clist, row, 0);
713 gtk_clist_moveto(clist, row, 0, 0.5, 0);
719 /****************************************************************************/
720 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
726 if (user_data->dlg.graph_window != NULL) {
727 /* There's already a graph window; reactivate it. */
728 reactivate_window(user_data->dlg.graph_window);
732 list = g_list_append(list, &(user_data->series_fwd));
733 list = g_list_append(list, &(user_data->series_rev));
735 user_data->series_fwd.color.pixel = 0;
736 user_data->series_fwd.color.red = 0x80ff;
737 user_data->series_fwd.color.green = 0xe0ff;
738 user_data->series_fwd.color.blue = 0xffff;
739 user_data->series_fwd.yvalue = 0.5;
741 user_data->series_rev.color.pixel = 0;
742 user_data->series_rev.color.red = 0x60ff;
743 user_data->series_rev.color.green = 0xc0ff;
744 user_data->series_rev.color.blue = 0xffff;
745 user_data->series_rev.yvalue = -0.5;
747 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
748 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
749 user_data->port_src_fwd,
750 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
751 user_data->port_dst_fwd,
752 user_data->ssrc_fwd);
754 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
755 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
756 user_data->port_src_rev,
757 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
758 user_data->port_dst_rev,
759 user_data->ssrc_rev);
761 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
762 &graph_selection_callback, user_data);
763 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
764 on_destroy_graph, user_data);
766 #endif /*USE_CONVERSATION_GRAPH*/
769 /****************************************************************************/
770 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
774 if (user_data->dlg.selected_clist!=NULL) {
775 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
776 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
777 goto_frame(&cfile, fnumber);
782 static void draw_stat(user_data_t *user_data);
784 /****************************************************************************/
785 /* re-dissects all packets */
786 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
788 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
789 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
790 redissect_packets(&cfile);
791 draw_stat(user_data);
794 /****************************************************************************/
795 /* on_destroy is automatically called after that */
796 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
798 gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
799 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
802 /****************************************************************************/
803 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
808 if (user_data->dlg.selected_clist==NULL)
811 if (user_data->dlg.selected_row==-1)
812 user_data->dlg.selected_row = 0;
814 clist = user_data->dlg.selected_clist;
815 row = user_data->dlg.selected_row + 1;
817 while (gtk_clist_get_text(clist,row,5,&text)) {
818 if (strcmp(text, OK_TEXT) != 0) {
819 gtk_clist_select_row(clist, row, 0);
820 gtk_clist_moveto(clist, row, 0, 0.5, 0);
828 while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
829 if (strcmp(text, OK_TEXT) != 0) {
830 gtk_clist_select_row(clist, row, 0);
831 gtk_clist_moveto(clist, row, 0, 0.5, 0);
838 /****************************************************************************/
839 /* when we want to save the information */
840 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
843 GtkWidget *rev, *forw, *both;
844 user_data_t *user_data;
850 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
852 /* Perhaps the user specified a directory instead of a file.
853 Check whether they did. */
854 if (test_for_directory(g_dest) == EISDIR) {
855 /* It's a directory - set the file selection box to display it. */
856 set_last_open_dir(g_dest);
858 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
862 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
863 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
864 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
865 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
867 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
868 fp = fopen(g_dest, "w");
870 if (GTK_TOGGLE_BUTTON(both)->active) {
871 fprintf(fp, "Forward\n");
874 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
876 fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
878 fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
882 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
883 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
884 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
886 fprintf(fp,"%s",columnText);
888 fprintf(fp,",%s",columnText);
897 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
899 if (GTK_TOGGLE_BUTTON(both)->active) {
900 fp = fopen(g_dest, "a");
901 fprintf(fp, "\nReverse\n");
903 fp = fopen(g_dest, "w");
905 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
907 fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
909 fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
913 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
914 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
915 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
917 fprintf(fp,"%s",columnText);
919 fprintf(fp,",%s",columnText);
927 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
930 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
932 user_data->dlg.save_csv_as_w = NULL;
935 /* when the user wants to save the csv information in a file */
936 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
940 GtkWidget *label_format;
941 GtkWidget *channels_label;
942 GSList *channels_group = NULL;
943 GtkWidget *forward_rb;
944 GtkWidget *reversed_rb;
948 if (user_data->dlg.save_csv_as_w != NULL) {
949 /* There's already a Save CSV info dialog box; reactivate it. */
950 reactivate_window(user_data->dlg.save_csv_as_w);
954 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
955 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
956 save_csv_as_destroy_cb, user_data);
958 /* Container for each row of widgets */
959 vertb = gtk_vbox_new(FALSE, 0);
960 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
961 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
962 vertb, FALSE, FALSE, 0);
963 gtk_widget_show (vertb);
965 table1 = gtk_table_new (2, 4, FALSE);
966 gtk_widget_show (table1);
967 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
968 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
969 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
971 label_format = gtk_label_new ("Format: Comma Separated Values");
972 gtk_widget_show (label_format);
973 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
974 (GtkAttachOptions) (GTK_FILL),
975 (GtkAttachOptions) (0), 0, 0);
978 channels_label = gtk_label_new ("Channels:");
979 gtk_widget_show (channels_label);
980 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
981 (GtkAttachOptions) (GTK_FILL),
982 (GtkAttachOptions) (0), 0, 0);
983 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
985 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
986 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
987 gtk_widget_show (forward_rb);
988 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
989 (GtkAttachOptions) (GTK_FILL),
990 (GtkAttachOptions) (0), 0, 0);
992 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
993 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
994 gtk_widget_show (reversed_rb);
995 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
996 (GtkAttachOptions) (GTK_FILL),
997 (GtkAttachOptions) (0), 0, 0);
999 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1000 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1001 gtk_widget_show (both_rb);
1002 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1003 (GtkAttachOptions) (GTK_FILL),
1004 (GtkAttachOptions) (0), 0, 0);
1006 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1008 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1009 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1010 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1011 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1012 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1014 /* Connect the cancel_button to destroy the widget */
1015 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1016 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1017 user_data->dlg.save_csv_as_w);
1019 /* Catch the "key_press_event" signal in the window, so that we can catch
1020 the ESC key being pressed and act as if the "Cancel" button had
1022 dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1024 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
1025 user_data->dlg.save_csv_as_w);
1027 gtk_widget_show(user_data->dlg.save_csv_as_w);
1031 /****************************************************************************/
1032 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1034 /* Note that we no longer have a Save voice info dialog box. */
1035 user_data->dlg.save_voice_as_w = NULL;
1038 /****************************************************************************/
1039 /* here we save it into a file that user specified */
1040 /* XXX what about endians here? could go something wrong? */
1041 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1043 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1047 guint32 f_write_silence = 0;
1048 guint32 r_write_silence = 0;
1050 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1051 gboolean stop_flag = FALSE;
1053 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1056 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1062 /* open file for saving */
1063 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1070 progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag);
1072 /* First we write the .au header. XXX Hope this is endian independant */
1073 /* the magic word 0x2e736e64 == .snd */
1074 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1075 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1076 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1077 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1078 /* header offset == 24 bytes */
1079 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1080 write(to_fd, pd, 1);
1081 write(to_fd, pd, 1);
1082 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1083 /* total length, it is permited to set this to 0xffffffff */
1084 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1085 write(to_fd, pd, 1);
1086 write(to_fd, pd, 1);
1087 write(to_fd, pd, 1);
1088 /* encoding format == 8 bit ulaw */
1089 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1090 write(to_fd, pd, 1);
1091 write(to_fd, pd, 1);
1092 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1093 /* sample rate == 8000 Hz */
1094 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1095 write(to_fd, pd, 1);
1096 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1097 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1099 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1100 write(to_fd, pd, 1);
1101 write(to_fd, pd, 1);
1102 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1105 /* only forward direction */
1107 progbar_count = user_data->forward.saveinfo.count;
1108 progbar_quantum = user_data->forward.saveinfo.count/100;
1109 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1112 if((count > progbar_nextstep) && (count <= progbar_count)) {
1113 update_progress_dlg(progbar,
1114 (gfloat) count/progbar_count, "Saving");
1115 progbar_nextstep = progbar_nextstep + progbar_quantum;
1118 *pd = (unsigned char)linear2ulaw(f_pd);
1119 fwritten = write(to_fd, pd, 1);
1120 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1124 destroy_progress_dlg(progbar);
1130 /* only reversed direction */
1132 progbar_count = user_data->reversed.saveinfo.count;
1133 progbar_quantum = user_data->reversed.saveinfo.count/100;
1134 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1137 if((count > progbar_nextstep) && (count <= progbar_count)) {
1138 update_progress_dlg(progbar,
1139 (gfloat) count/progbar_count, "Saving");
1140 progbar_nextstep = progbar_nextstep + progbar_quantum;
1143 *pd = (unsigned char)linear2ulaw(r_pd);
1144 rwritten = write(to_fd, pd, 1);
1145 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1149 destroy_progress_dlg(progbar);
1155 /* both directions */
1157 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
1158 (progbar_count = user_data->forward.saveinfo.count) :
1159 (progbar_count = user_data->reversed.saveinfo.count);
1160 progbar_quantum = progbar_count/100;
1161 /* since conversation in one way can start later than in the other one,
1162 * we have to write some silence information for one channel */
1163 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1164 f_write_silence = (guint32)
1165 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
1167 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1168 r_write_silence = (guint32)
1169 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
1174 if((count > progbar_nextstep) && (count <= progbar_count)) {
1175 update_progress_dlg(progbar,
1176 (gfloat) count/progbar_count, "Saving");
1177 progbar_nextstep = progbar_nextstep + progbar_quantum;
1180 if(f_write_silence > 0) {
1181 rread = read(rev_fd, &r_pd, 2);
1186 else if(r_write_silence > 0) {
1187 fread = read(forw_fd, &f_pd, 2);
1193 fread = read(forw_fd, &f_pd, 2);
1194 rread = read(rev_fd, &r_pd, 2);
1196 if ((rread == 0) && (fread == 0))
1198 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1199 rwritten = write(to_fd, pd, 1);
1200 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1204 destroy_progress_dlg(progbar);
1210 destroy_progress_dlg(progbar);
1218 /****************************************************************************/
1219 /* the user wants to save in a file */
1220 /* XXX support for different formats is currently commented out */
1221 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1224 /*GtkWidget *wav, *au, *sw;*/
1225 GtkWidget *rev, *forw, *both;
1226 user_data_t *user_data;
1227 gint channels /*, format*/;
1229 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1231 /* Perhaps the user specified a directory instead of a file.
1232 Check whether they did. */
1233 if (test_for_directory(g_dest) == EISDIR) {
1234 /* It's a directory - set the file selection box to display it. */
1235 set_last_open_dir(g_dest);
1237 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1241 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1242 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1243 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1244 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1245 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1246 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1247 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1249 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1250 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1251 * disable the ok button or disable the buttons for direction if only one is not ok. The
1252 * problem is if we open the save voice dialog and then click the refresh button and maybe
1253 * the state changes, so we can't save anymore. In this case we should be able to update
1254 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1257 /* we can not save in both dirctions */
1258 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1259 /* there are many combinations here, we just exit when first matches */
1260 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
1261 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1262 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1263 "Can't save in a file: Unsupported codec!");
1264 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
1265 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1266 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1267 "Can't save in a file: Wrong length of captured packets!");
1268 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
1269 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1270 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1271 "Can't save in a file: RTP data with padding!");
1273 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1274 "Can't save in a file: File I/O problem!");
1277 /* we can not save forward direction */
1278 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1279 (GTK_TOGGLE_BUTTON (both)->active))) {
1280 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1281 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1282 "Can't save forward direction in a file: Unsupported codec!");
1283 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1284 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1285 "Can't save forward direction in a file: Wrong length of captured packets!");
1286 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1287 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1288 "Can't save forward direction in a file: RTP data with padding!");
1290 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1291 "Can't save forward direction in a file: File I/O problem!");
1294 /* we can not save reversed direction */
1295 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1296 (GTK_TOGGLE_BUTTON (both)->active))) {
1297 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1298 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1299 "Can't save reversed direction in a file: Unsupported codec!");
1300 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1301 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1302 "Can't save reversed direction in a file: Wrong length of captured packets!");
1303 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1304 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1305 "Can't save reversed direction in a file: RTP data with padding!");
1306 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1307 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1308 "Can't save reversed direction in a file: No RTP data!");
1310 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1311 "Can't save reversed direction in a file: File I/O problem!");
1315 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1317 else if (GTK_TOGGLE_BUTTON (au)->active)
1319 else if (GTK_TOGGLE_BUTTON (sw)->active)
1322 if (GTK_TOGGLE_BUTTON (rev)->active)
1324 else if (GTK_TOGGLE_BUTTON (both)->active)
1329 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1330 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1331 "An error occured while saving voice in a file!");
1335 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1338 /****************************************************************************/
1339 /* when the user wants to save the voice information in a file */
1340 /* XXX support for different formats is currently commented out */
1341 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1345 GtkWidget *label_format;
1346 GtkWidget *channels_label;
1347 /*GSList *format_group = NULL;*/
1348 GSList *channels_group = NULL;
1349 GtkWidget *forward_rb;
1350 GtkWidget *reversed_rb;
1352 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1355 /* if we can't save in a file: wrong codec, cut packets or other errors */
1356 /* shold the error arise here or later when you click ok button ?
1357 * if we do it here, then we must disable the refresh button, so we don't do it here */
1359 if (user_data->dlg.save_voice_as_w != NULL) {
1360 /* There's already a Save voice info dialog box; reactivate it. */
1361 reactivate_window(user_data->dlg.save_voice_as_w);
1365 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1366 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
1367 save_voice_as_destroy_cb, user_data);
1369 /* Container for each row of widgets */
1370 vertb = gtk_vbox_new(FALSE, 0);
1371 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1372 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1373 vertb, FALSE, FALSE, 0);
1374 gtk_widget_show (vertb);
1376 table1 = gtk_table_new (2, 4, FALSE);
1377 gtk_widget_show (table1);
1378 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1379 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1380 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1382 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1383 gtk_widget_show (label_format);
1384 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1385 (GtkAttachOptions) (GTK_FILL),
1386 (GtkAttachOptions) (0), 0, 0);
1388 /* we support .au - ulaw*/
1389 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1390 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1391 gtk_widget_show (wav_rb);
1392 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1393 (GtkAttachOptions) (GTK_FILL),
1394 (GtkAttachOptions) (0), 0, 0);
1396 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1397 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1398 gtk_widget_show (sw_rb);
1399 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1400 (GtkAttachOptions) (GTK_FILL),
1401 (GtkAttachOptions) (0), 0, 0);
1402 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1403 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1404 gtk_widget_show (au_rb);
1405 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1406 (GtkAttachOptions) (GTK_FILL),
1407 (GtkAttachOptions) (0), 0, 0);
1410 channels_label = gtk_label_new ("Channels:");
1411 gtk_widget_show (channels_label);
1412 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1413 (GtkAttachOptions) (GTK_FILL),
1414 (GtkAttachOptions) (0), 0, 0);
1415 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1417 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1418 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1419 gtk_widget_show (forward_rb);
1420 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1421 (GtkAttachOptions) (GTK_FILL),
1422 (GtkAttachOptions) (0), 0, 0);
1424 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1425 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1426 gtk_widget_show (reversed_rb);
1427 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1428 (GtkAttachOptions) (GTK_FILL),
1429 (GtkAttachOptions) (0), 0, 0);
1431 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1432 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1433 gtk_widget_show (both_rb);
1434 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1435 (GtkAttachOptions) (GTK_FILL),
1436 (GtkAttachOptions) (0), 0, 0);
1438 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1440 /* if one direction is nok we don't allow saving
1441 XXX this is not ok since the user can click the refresh button and cause changes
1442 but we can not update this window. So we move all the decision on the time the ok
1444 if (user_data->forward.saved == FALSE) {
1445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1446 gtk_widget_set_sensitive(forward_rb, FALSE);
1447 gtk_widget_set_sensitive(both_rb, FALSE);
1449 else if (user_data->reversed.saved == FALSE) {
1450 gtk_widget_set_sensitive(reversed_rb, FALSE);
1451 gtk_widget_set_sensitive(both_rb, FALSE);
1455 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1456 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1457 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1458 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1459 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1460 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1461 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1462 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1464 /* Connect the cancel_button to destroy the widget */
1465 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1466 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1467 user_data->dlg.save_voice_as_w);
1469 /* Catch the "key_press_event" signal in the window, so that we can catch
1470 the ESC key being pressed and act as if the "Cancel" button had
1472 dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1474 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
1475 user_data->dlg.save_voice_as_w);
1477 gtk_widget_show(user_data->dlg.save_voice_as_w);
1481 /****************************************************************************/
1482 /* when we are finished with redisection, we add the label for the statistic */
1483 static void draw_stat(user_data_t *user_data)
1485 gchar label_max[200];
1486 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1487 - user_data->forward.statinfo.start_seq_nr + 1;
1488 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1489 - user_data->reversed.statinfo.start_seq_nr + 1;
1490 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1491 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1493 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
1494 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1495 " Sequence errors = %u",
1496 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr, user_data->forward.statinfo.total_nr,
1497 f_expected, f_lost, user_data->forward.statinfo.sequence);
1499 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1501 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
1502 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1503 " Sequence errors = %u",
1504 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr, user_data->reversed.statinfo.total_nr,
1505 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1507 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1512 /****************************************************************************/
1513 /* append a line to clist */
1514 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1515 double delay, double jitter, gchar *status, gboolean marker,
1516 gchar *timeStr, guint32 pkt_len, GdkColor *color)
1522 data[0]=&field[0][0];
1523 data[1]=&field[1][0];
1524 data[2]=&field[2][0];
1525 data[3]=&field[3][0];
1526 data[4]=&field[4][0];
1527 data[5]=&field[5][0];
1528 data[6]=&field[6][0];
1529 data[7]=&field[7][0];
1531 g_snprintf(field[0], 20, "%u", number);
1532 g_snprintf(field[1], 20, "%u", seq_num);
1533 g_snprintf(field[2], 20, "%f", delay);
1534 g_snprintf(field[3], 20, "%f", jitter);
1535 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1536 g_snprintf(field[5], 40, "%s", status);
1537 g_snprintf(field[6], 32, "%s", timeStr);
1538 g_snprintf(field[7], 20, "%u", pkt_len);
1540 added_row = gtk_clist_append(GTK_CLIST(clist), data);
1541 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1542 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1545 /****************************************************************************/
1546 /* Create the dialog box with all widgets */
1547 void create_rtp_dialog(user_data_t* user_data)
1549 GtkWidget *window = NULL;
1550 GtkWidget *clist_fwd;
1551 GtkWidget *clist_rev;
1552 GtkWidget *label_stats_fwd;
1553 GtkWidget *label_stats_rev;
1554 GtkWidget *notebook;
1556 GtkWidget *main_vb, *page, *page_r, *label, *label1, *label2, *label3;
1557 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1558 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1559 #ifdef USE_CONVERSATION_GRAPH
1560 GtkWidget *graph_bt;
1563 gchar *titles[8] = {"Packet", "Sequence", "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"};
1564 gchar label_forward[150];
1565 gchar label_reverse[150];
1567 gchar str_ip_src[16];
1568 gchar str_ip_dst[16];
1571 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1572 gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1573 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1574 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
1576 /* Container for each row of widgets */
1577 main_vb = gtk_vbox_new(FALSE, 3);
1578 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1579 gtk_container_add(GTK_CONTAINER(window), main_vb);
1580 gtk_widget_show(main_vb);
1584 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1585 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1587 g_snprintf(label_forward, 149,
1588 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1589 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1591 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1592 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1594 g_snprintf(label_reverse, 149,
1595 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1596 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1598 /* Start a notebook for flipping between sets of changes */
1599 notebook = gtk_notebook_new();
1600 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1601 OBJECT_SET_DATA(window, "notebook", notebook);
1602 SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
1605 /* page for forward connection */
1606 page = gtk_vbox_new(FALSE, 5);
1607 gtk_container_set_border_width(GTK_CONTAINER(page), 20);
1609 /* scrolled window */
1610 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1611 WIDGET_SET_SIZE(scrolled_window, 600, 200);
1612 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1613 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1615 /* direction label */
1616 label1 = gtk_label_new(label_forward);
1617 gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
1619 /* place for some statistics */
1620 label_stats_fwd = gtk_label_new("\n\n");
1621 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 5);
1623 /* clist for the information */
1624 clist_fwd = gtk_clist_new_with_titles(8, titles);
1625 gtk_widget_show(clist_fwd);
1626 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1627 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1628 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
1629 /* Hide date and length column */
1630 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1631 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1634 label = gtk_label_new(" Forward Direction ");
1635 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1637 /* column width and justification */
1638 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 80);
1639 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 80);
1640 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 80);
1641 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 80);
1642 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 40);
1643 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_CENTER);
1644 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_CENTER);
1645 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1646 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1647 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1648 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1650 /* same page for reversed connection */
1651 page_r = gtk_vbox_new(FALSE, 5);
1652 gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
1653 scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
1654 WIDGET_SET_SIZE(scrolled_window_r, 600, 200);
1655 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
1656 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1657 label3 = gtk_label_new(label_reverse);
1658 gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
1659 label_stats_rev = gtk_label_new("\n\n");
1660 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 5);
1661 clist_rev = gtk_clist_new_with_titles(8, titles);
1662 gtk_widget_show(clist_rev);
1663 gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 6, FALSE);
1664 gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 7, FALSE);
1666 SIGNAL_CONNECT(clist_rev, "select_row", on_clist_select_row, user_data);
1668 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
1669 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
1670 label2 = gtk_label_new(" Reversed Direction ");
1671 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
1673 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 0, 80);
1674 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 1, 80);
1675 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 2, 80);
1676 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 3, 80);
1677 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 4, 40);
1678 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 0, GTK_JUSTIFY_CENTER);
1679 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 1, GTK_JUSTIFY_CENTER);
1680 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 2, GTK_JUSTIFY_CENTER);
1681 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 3, GTK_JUSTIFY_CENTER);
1682 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 4, GTK_JUSTIFY_CENTER);
1683 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 5, GTK_JUSTIFY_CENTER);
1685 /* page for help&about or future
1686 page_help = gtk_hbox_new(FALSE, 5);
1687 label4 = gtk_label_new(" Future ");
1688 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
1689 frame = gtk_frame_new("");
1690 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
1691 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
1692 gtk_container_add(GTK_CONTAINER(frame), text);
1693 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
1694 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
1697 /* show all notebooks */
1698 gtk_widget_show_all(notebook);
1701 box4 = gtk_hbutton_box_new();
1702 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
1703 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
1704 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
1705 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
1706 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
1707 gtk_widget_show(box4);
1709 voice_bt = gtk_button_new_with_label("Save payload...");
1710 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
1711 gtk_widget_show(voice_bt);
1712 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
1714 csv_bt = gtk_button_new_with_label("Save as CSV...");
1715 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
1716 gtk_widget_show(csv_bt);
1717 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
1719 refresh_bt = gtk_button_new_with_label("Refresh");
1720 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
1721 gtk_widget_show(refresh_bt);
1722 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
1724 goto_bt = gtk_button_new_with_label("Go to frame");
1725 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
1726 gtk_widget_show(goto_bt);
1727 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
1729 #ifdef USE_CONVERSATION_GRAPH
1730 graph_bt = gtk_button_new_with_label("Graph");
1731 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
1732 gtk_widget_show(graph_bt);
1733 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
1736 next_bt = gtk_button_new_with_label("Next");
1737 gtk_container_add(GTK_CONTAINER(box4), next_bt);
1738 gtk_widget_show(next_bt);
1739 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
1741 close_bt = gtk_button_new_with_label("Close");
1742 gtk_container_add(GTK_CONTAINER(box4), close_bt);
1743 gtk_widget_show(close_bt);
1744 SIGNAL_CONNECT(close_bt, "clicked", on_close_bt_clicked, user_data);
1746 gtk_widget_show(window);
1748 user_data->dlg.window = window;
1749 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
1750 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
1751 user_data->dlg.label_stats_fwd = label_stats_fwd;
1752 user_data->dlg.label_stats_rev = label_stats_rev;
1753 user_data->dlg.notebook = notebook;
1754 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
1755 user_data->dlg.selected_row = 0;
1759 /****************************************************************************/
1760 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
1761 const gchar* proto_field, guint32* p_result)
1764 proto_node *proto_sibling_node;
1765 header_field_info *hfssrc;
1768 finfo = PITEM_FINFO(ptree_node);
1770 if (hfinformation==(finfo->hfinfo)) {
1771 hfssrc = proto_registrar_get_byname((gchar*) proto_field);
1774 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
1775 ptree_node=ptree_node->next) {
1776 finfo=PITEM_FINFO(ptree_node);
1777 if (hfssrc==finfo->hfinfo) {
1778 if (hfinformation->type==FT_IPv4) {
1779 ipv4 = fvalue_get(&finfo->value);
1780 *p_result = ipv4_get_net_order_addr(ipv4);
1783 *p_result = fvalue_get_integer(&finfo->value);
1790 proto_sibling_node = ptree_node->next;
1792 if (proto_sibling_node) {
1793 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
1799 /****************************************************************************/
1800 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
1801 const gchar* proto_name,
1802 const gchar* proto_field,
1805 proto_node *ptree_node;
1806 header_field_info *hfinformation;
1808 hfinformation = proto_registrar_get_byname((gchar*) proto_name);
1809 if (hfinformation == NULL)
1812 ptree_node = ((proto_node *)protocol_tree)->first_child;
1816 return process_node(ptree_node, hfinformation, proto_field, p_result);
1820 /****************************************************************************/
1821 /* XXX only handles RTP over IPv4, should add IPv6 support */
1824 guint16 port_src_fwd,
1826 guint16 port_dst_fwd,
1829 guint16 port_src_rev,
1831 guint16 port_dst_rev,
1835 user_data_t *user_data;
1836 gchar filter_text[256];
1838 GString *error_string;
1840 user_data = g_malloc(sizeof(user_data_t));
1842 user_data->ip_src_fwd = ip_src_fwd;
1843 user_data->port_src_fwd = port_src_fwd;
1844 user_data->ip_dst_fwd = ip_dst_fwd;
1845 user_data->port_dst_fwd = port_dst_fwd;
1846 user_data->ssrc_fwd = ssrc_fwd;
1847 user_data->ip_src_rev = ip_src_rev;
1848 user_data->port_src_rev = port_src_rev;
1849 user_data->ip_dst_rev = ip_dst_rev;
1850 user_data->port_dst_rev = port_dst_rev;
1851 user_data->ssrc_rev = ssrc_rev;
1853 create_rtp_dialog(user_data);
1855 /* Try to compile the filter. */
1856 strcpy(filter_text,"rtp && ip");
1857 if (!dfilter_compile(filter_text, &sfcode)) {
1858 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
1862 sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ) || ( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ))",
1863 ip_to_str((ip_addr_p)&ip_src_fwd),
1865 ip_to_str((ip_addr_p)&ip_dst_fwd),
1867 ip_to_str((ip_addr_p)&ip_src_rev),
1869 ip_to_str((ip_addr_p)&ip_dst_rev),
1873 error_string = register_tap_listener("rtp", user_data, filter_text,
1874 (void*)rtp_reset, (void*)rtp_packet, (void*)rtp_draw);
1875 if (error_string != NULL) {
1876 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
1877 g_string_free(error_string, TRUE);
1883 /* file names for storing sound data */
1884 strncpy(user_data->f_tempname, "f_tempnameXXXXXX", TMPNAMSIZE);
1885 strncpy(user_data->r_tempname, "r_tempnameXXXXXX", TMPNAMSIZE);
1886 mkstemp(user_data->f_tempname);
1887 mkstemp(user_data->r_tempname);
1888 user_data->forward.saveinfo.fp = NULL;
1889 user_data->reversed.saveinfo.fp = NULL;
1890 user_data->dlg.save_voice_as_w = NULL;
1891 user_data->dlg.save_csv_as_w = NULL;
1892 #ifdef USE_CONVERSATION_GRAPH
1893 user_data->dlg.graph_window = NULL;
1894 user_data->series_fwd.value_pairs = NULL;
1895 user_data->series_rev.value_pairs = NULL;
1898 redissect_packets(&cfile);
1900 draw_stat(user_data);
1903 /****************************************************************************/
1904 /* entry point from main menu */
1905 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
1908 guint16 port_src_fwd;
1910 guint16 port_dst_fwd;
1911 guint32 ssrc_fwd = 0;
1913 guint16 port_src_rev;
1915 guint16 port_dst_rev;
1916 guint32 ssrc_rev = 0;
1918 gchar filter_text[256];
1921 epan_dissect_t *edt;
1923 gboolean frame_matched;
1925 GList *strinfo_list;
1926 GList *filtered_list = NULL;
1927 rtp_stream_info_t *strinfo;
1930 /* Try to compile the filter. */
1931 strcpy(filter_text,"rtp && ip");
1932 if (!dfilter_compile(filter_text, &sfcode)) {
1933 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
1936 /* we load the current file into cf variable */
1938 fdata = cf->current_frame;
1940 /* we are on the selected frame now */
1942 return; /* if we exit here it's an error */
1944 /* dissect the current frame */
1945 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) {
1946 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1947 file_read_error_message(err), cf->filename);
1950 edt = epan_dissect_new(TRUE, FALSE);
1951 epan_dissect_prime_dfilter(edt, sfcode);
1952 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
1953 frame_matched = dfilter_apply_edt(sfcode, edt);
1955 /* if it is not an rtp frame, show the rtpstream dialog */
1956 frame_matched = dfilter_apply_edt(sfcode, edt);
1957 if (frame_matched != 1) {
1958 rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
1961 epan_dissect_free(edt);
1962 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!");
1967 /* ok, it is a RTP frame, so let's get the ip and port values */
1968 g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
1969 g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
1970 port_src_fwd = edt->pi.srcport;
1971 port_dst_fwd = edt->pi.destport;
1973 /* assume the inverse ip/port combination for the reverse direction */
1974 g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
1975 g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
1976 port_src_rev = edt->pi.destport;
1977 port_dst_rev = edt->pi.srcport;
1979 /* now we need the SSRC value of the current frame */
1980 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
1981 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!");
1985 /* search for reversed direction in the global rtp streams list */
1987 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
1988 while (strinfo_list)
1990 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
1991 if (strinfo->src_addr==ip_src_fwd
1992 && strinfo->src_port==port_src_fwd
1993 && strinfo->dest_addr==ip_dst_fwd
1994 && strinfo->dest_port==port_dst_fwd)
1996 filtered_list = g_list_prepend(filtered_list, strinfo);
1999 if (strinfo->src_addr==ip_src_rev
2000 && strinfo->src_port==port_src_rev
2001 && strinfo->dest_addr==ip_dst_rev
2002 && strinfo->dest_port==port_dst_rev)
2005 filtered_list = g_list_append(filtered_list, strinfo);
2007 ssrc_rev = strinfo->ssrc;
2010 strinfo_list = g_list_next(strinfo_list);
2013 /* if more than one reverse streams found, we let the user choose the right one */
2015 rtpstream_dlg_show(filtered_list);
2034 /****************************************************************************/
2036 rtp_analysis_init(char *dummy _U_)
2038 rtp_analysis_cb(NULL, NULL);
2041 /****************************************************************************/
2043 register_tap_listener_rtp_analysis(void)
2045 register_ethereal_tap("rtp", rtp_analysis_init);
2049 register_tap_menu_rtp_analysis(void)
2051 register_tap_menu_item("Statistics/RTP Streams/Analyse...",
2052 rtp_analysis_cb, NULL, NULL, NULL);