2 * RTP analysis addition for ethereal
4 * $Id: rtp_analysis.c,v 1.6 2003/10/03 22:38:56 guy 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 guint32 flags; /* see STAT_FLAG-defines below */
118 gboolean first_packet;
121 guint32 delta_timestamp;
128 guint16 start_seq_nr;
137 /* status flags for the flags parameter in tap_rtp_stat_t */
138 #define STAT_FLAG_FIRST 0x01
139 #define STAT_FLAG_MARKER 0x02
140 #define STAT_FLAG_WRONG_SEQ 0x04
141 #define STAT_FLAG_PT_CHANGE 0x08
142 #define STAT_FLAG_PT_CN 0x10
144 typedef struct _tap_rtp_save_info_t {
147 error_type_t error_type;
149 } tap_rtp_save_info_t;
152 /* structure that holds the information about the forward and reversed direction */
153 struct _info_direction {
154 tap_rtp_stat_t statinfo;
155 tap_rtp_save_info_t saveinfo;
158 #define TMPNAMSIZE 100
160 /* structure that holds general information about the connection
161 * and structures for both directions */
162 typedef struct _user_data_t {
163 /* tap associated data*/
165 guint16 port_src_fwd;
167 guint16 port_dst_fwd;
170 guint16 port_src_rev;
172 guint16 port_dst_rev;
175 struct _info_direction forward;
176 struct _info_direction reversed;
178 char f_tempname[TMPNAMSIZE];
179 char r_tempname[TMPNAMSIZE];
181 /* dialog associated data */
184 #ifdef USE_CONVERSATION_GRAPH
185 time_series_t series_fwd;
186 time_series_t series_rev;
191 typedef const guint8 * ip_addr_p;
194 /****************************************************************************/
197 /****************************************************************************/
198 /* when there is a [re]reading of packet's */
200 rtp_reset(user_data_t *user_data _U_)
202 user_data->forward.statinfo.first_packet = TRUE;
203 user_data->reversed.statinfo.first_packet = TRUE;
204 user_data->forward.statinfo.max_delay = 0;
205 user_data->reversed.statinfo.max_delay = 0;
206 user_data->forward.statinfo.delay = 0;
207 user_data->reversed.statinfo.delay = 0;
208 user_data->forward.statinfo.jitter = 0;
209 user_data->reversed.statinfo.jitter = 0;
210 user_data->forward.statinfo.timestamp = 0;
211 user_data->reversed.statinfo.timestamp = 0;
212 user_data->forward.statinfo.max_nr = 0;
213 user_data->reversed.statinfo.max_nr = 0;
214 user_data->forward.statinfo.total_nr = 0;
215 user_data->reversed.statinfo.total_nr = 0;
216 user_data->forward.statinfo.sequence = 0;
217 user_data->reversed.statinfo.sequence = 0;
218 user_data->forward.statinfo.start_seq_nr = 0;
219 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
220 user_data->forward.statinfo.stop_seq_nr = 0;
221 user_data->reversed.statinfo.stop_seq_nr = 0;
222 user_data->forward.statinfo.cycles = 0;
223 user_data->reversed.statinfo.cycles = 0;
224 user_data->forward.statinfo.under = FALSE;
225 user_data->reversed.statinfo.under = FALSE;
226 user_data->forward.statinfo.start_time = 0;
227 user_data->reversed.statinfo.start_time = 0;
228 user_data->forward.statinfo.time = 0;
229 user_data->reversed.statinfo.time = 0;
231 user_data->forward.saveinfo.count = 0;
232 user_data->reversed.saveinfo.count = 0;
233 user_data->forward.saveinfo.saved = FALSE;
234 user_data->reversed.saveinfo.saved = FALSE;
236 #ifdef USE_CONVERSATION_GRAPH
237 if (user_data->dlg.graph_window != NULL)
238 gtk_widget_destroy(user_data->dlg.graph_window);
240 g_array_free(user_data->series_fwd.value_pairs, TRUE);
241 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
243 g_array_free(user_data->series_rev.value_pairs, TRUE);
244 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
247 /* XXX check for error at fclose? */
248 if (user_data->forward.saveinfo.fp != NULL)
249 fclose(user_data->forward.saveinfo.fp);
250 if (user_data->reversed.saveinfo.fp != NULL)
251 fclose(user_data->reversed.saveinfo.fp);
252 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
253 if (user_data->forward.saveinfo.fp == NULL)
254 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
255 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
256 if (user_data->reversed.saveinfo.fp == NULL)
257 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
261 /****************************************************************************/
262 /* here we can redraw the output */
264 static void rtp_draw(void *prs _U_)
269 /* forward declarations */
270 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
271 double delay, double jitter, gchar *status, gboolean marker,
272 gchar *timeStr, guint32 pkt_len, GdkColor *color);
274 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
275 packet_info *pinfo, struct _rtp_info *rtpinfo);
276 static int rtp_packet_add_info(GtkCList *clist,
277 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
278 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
279 tap_rtp_stat_t *statinfo,
280 packet_info *pinfo, struct _rtp_info *rtpinfo);
283 /****************************************************************************/
284 /* whenever a RTP packet is seen by the tap listener */
285 static int rtp_packet(user_data_t *user_data, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo)
287 #ifdef USE_CONVERSATION_GRAPH
291 /* we ignore packets that are not displayed */
292 if (pinfo->fd->flags.passed_dfilter == 0)
295 /* is it the forward direction? */
296 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
297 #ifdef USE_CONVERSATION_GRAPH
298 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
299 vp.fnumber = pinfo->fd->num;
300 g_array_append_val(user_data->series_fwd.value_pairs, vp);
302 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
303 rtp_packet_add_info(user_data->dlg.clist_fwd,
304 &(user_data->forward.statinfo), pinfo, rtpinfo);
305 rtp_packet_save_payload(&(user_data->forward.saveinfo),
306 &(user_data->forward.statinfo), pinfo, rtpinfo);
308 /* is it the reversed direction? */
309 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
310 #ifdef USE_CONVERSATION_GRAPH
311 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
312 vp.fnumber = pinfo->fd->num;
313 g_array_append_val(user_data->series_rev.value_pairs, vp);
315 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
316 rtp_packet_add_info(user_data->dlg.clist_rev,
317 &(user_data->reversed.statinfo), pinfo, rtpinfo);
318 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
319 &(user_data->reversed.statinfo), pinfo, rtpinfo);
326 /****************************************************************************/
327 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
328 packet_info *pinfo, struct _rtp_info *rtpinfo)
331 double current_jitter;
332 gboolean first_packet = statinfo->first_packet;
336 /* check payload type */
337 if (rtpinfo->info_payload_type == PT_CN
338 || rtpinfo->info_payload_type == PT_CN_OLD)
339 statinfo->flags |= STAT_FLAG_PT_CN;
340 if (rtpinfo->info_payload_type != statinfo->pt)
341 statinfo->flags |= STAT_FLAG_PT_CHANGE;
343 statinfo->pt = rtpinfo->info_payload_type;
345 /* store the current time and calculate the current jitter */
346 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
347 current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
348 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
349 statinfo->delay = current_time-(statinfo->time);
350 statinfo->jitter = current_jitter;
352 /* is this the first packet we got in this direction? */
354 statinfo->first_packet = FALSE;
355 statinfo->start_seq_nr = rtpinfo->info_seq_num;
356 statinfo->start_time = current_time;
358 statinfo->jitter = 0;
359 statinfo->flags |= STAT_FLAG_FIRST;
361 /* is it a packet with the mark bit set? */
362 if (rtpinfo->info_marker_set) {
363 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
364 statinfo->flags |= STAT_FLAG_MARKER;
366 /* if neither then it is a normal packet */
368 if (statinfo->delay > statinfo->max_delay) {
369 statinfo->max_delay = statinfo->delay;
370 statinfo->max_nr = pinfo->fd->num;
374 /* When calculating expected rtp packets the seq number can wrap around
375 * so we have to count the number of cycles
376 * Variable cycles counts the wraps around in forwarding connection and
377 * under is flag that indicates where we are
379 * XXX how to determine number of cycles with all possible lost, late
380 * and duplicated packets without any doubt? It seems to me, that
381 * because of all possible combination of late, duplicated or lost
382 * packets, this can only be more or less good approximation
384 * There are some combinations (rare but theoretically possible),
385 * where below code won't work correctly - statistic may be wrong then.
388 /* so if the current sequence number is less than the start one
389 * we assume, that there is another cycle running */
390 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
392 statinfo->under = TRUE;
394 /* what if the start seq nr was 0? Then the above condition will never
395 * be true, so we add another condition. XXX The problem would arise
396 * if one of the packets with seq nr 0 or 65535 would be lost or late */
397 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
398 (statinfo->under == FALSE)){
400 statinfo->under = TRUE;
402 /* the whole round is over, so reset the flag */
403 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
404 statinfo->under = FALSE;
407 /* Since it is difficult to count lost, duplicate or late packets separately,
408 * we would like to know at least how many times the sequence number was not ok */
410 /* if the current seq number equals the last one or if we are here for
411 * the first time, then it is ok, we just store the current one as the last one */
412 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (first_packet) )
413 statinfo->seq_num = rtpinfo->info_seq_num;
414 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
415 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
416 statinfo->seq_num = rtpinfo->info_seq_num;
418 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
419 statinfo->seq_num = rtpinfo->info_seq_num;
420 statinfo->sequence++;
421 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
423 /* late or duplicated */
424 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
425 statinfo->sequence++;
426 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
429 statinfo->time = current_time;
430 statinfo->timestamp = rtpinfo->info_timestamp;
431 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
432 statinfo->total_nr++;
438 /****************************************************************************/
439 /* adds statistics information from the packet to the clist */
440 static int rtp_packet_add_info(GtkCList *clist,
441 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
448 GdkColor color = {0, 0xffff, 0xffff, 0xffff};
450 then = pinfo->fd->abs_secs;
451 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
452 tm_tmp = localtime(&then);
453 snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
456 tm_tmp->tm_year + 1900,
462 if (statinfo->pt == PT_CN) {
463 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
466 color.green = 0x7fff;
469 else if (statinfo->pt == PT_CN_OLD) {
470 snprintf(status,40,"Comfort noise (PT=19, reserved)");
473 color.green = 0x7fff;
476 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
477 snprintf(status,40,"Wrong sequence nr.");
480 color.green = 0x7fff;
483 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
484 && !(statinfo->flags & STAT_FLAG_FIRST)
485 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
486 snprintf(status,40,"Payload type changed to PT=%u", statinfo->pt);
489 color.green = 0x7fff;
493 snprintf(status,40,OK_TEXT);
496 /* is this the first packet we got in this direction? */
497 if (statinfo->first_packet) {
499 pinfo->fd->num, rtpinfo->info_seq_num,
503 rtpinfo->info_marker_set,
504 timeStr, pinfo->fd->pkt_len,
509 pinfo->fd->num, rtpinfo->info_seq_num,
513 rtpinfo->info_marker_set,
514 timeStr, pinfo->fd->pkt_len,
522 /****************************************************************************/
523 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
524 tap_rtp_stat_t *statinfo,
525 packet_info *pinfo, struct _rtp_info *rtpinfo)
531 /* is this the first packet we got in this direction? */
532 if (statinfo->first_packet) {
533 if (saveinfo->fp == NULL) {
534 saveinfo->saved = FALSE;
535 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
538 saveinfo->saved = TRUE;
541 /* save the voice information */
542 /* if there was already an error, we quit */
543 if (saveinfo->saved == FALSE)
546 /* if the captured length and packet length aren't equal, we quit
547 * because there is some information missing */
548 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
549 saveinfo->saved = FALSE;
550 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
554 /* if padding bit is set, but the padding count is bigger
555 * then the whole RTP data - error with padding count */
556 if ( (rtpinfo->info_padding_set != FALSE) &&
557 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
558 saveinfo->saved = FALSE;
559 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
563 /* do we need to insert some silence? */
564 if ((rtpinfo->info_marker_set) &&
565 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
566 /* the amount of silence should be the difference between
567 * the last timestamp and the current one minus x
568 * x should equal the amount of information in the last frame
569 * XXX not done yet */
570 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
571 rtpinfo->info_padding_count); i++) {
572 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
573 fwrite(&tmp, 2, 1, saveinfo->fp);
576 fflush(saveinfo->fp);
580 if (rtpinfo->info_payload_type == 0) {
581 /* we put the pointer at the beggining of the RTP data, that is
582 * at the end of the current frame minus the length of the
583 * padding count minus length of the RTP data */
584 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
585 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
586 tmp = (gint16 )ulaw2linear((unsigned char)*data);
587 fwrite(&tmp, 2, 1, saveinfo->fp);
590 fflush(saveinfo->fp);
591 saveinfo->saved = TRUE;
596 else if (rtpinfo->info_payload_type == 8) {
597 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
598 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
599 tmp = (gint16 )alaw2linear((unsigned char)*data);
600 fwrite(&tmp, 2, 1, saveinfo->fp);
603 fflush(saveinfo->fp);
604 saveinfo->saved = TRUE;
607 /* comfort noise? - do nothing */
608 else if (rtpinfo->info_payload_type == PT_CN
609 && rtpinfo->info_payload_type == PT_CN_OLD) {
611 /* unsupported codec or XXX other error */
613 saveinfo->saved = FALSE;
614 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
622 /****************************************************************************/
625 /****************************************************************************/
626 /* XXX just copied from gtk/rpc_stat.c */
627 void protect_thread_critical_region(void);
628 void unprotect_thread_critical_region(void);
631 /****************************************************************************/
632 /* close the dialog window and remove the tap listener */
633 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
635 protect_thread_critical_region();
636 remove_tap_listener(user_data);
637 unprotect_thread_critical_region();
639 if (user_data->forward.saveinfo.fp != NULL)
640 fclose(user_data->forward.saveinfo.fp);
641 if (user_data->reversed.saveinfo.fp != NULL)
642 fclose(user_data->reversed.saveinfo.fp);
643 remove(user_data->f_tempname);
644 remove(user_data->r_tempname);
646 /* Is there a save voice window open? */
647 if (user_data->dlg.save_voice_as_w != NULL)
648 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
650 #ifdef USE_CONVERSATION_GRAPH
651 /* Is there a graph window open? */
652 if (user_data->dlg.graph_window != NULL)
653 gtk_widget_destroy(user_data->dlg.graph_window);
660 /****************************************************************************/
661 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
662 GtkNotebookPage *page _U_,
664 user_data_t *user_data _U_)
666 user_data->dlg.selected_clist =
667 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
668 user_data->dlg.selected_row = 0;
671 /****************************************************************************/
672 static void on_clist_select_row(GtkCList *clist _U_,
676 user_data_t *user_data _U_)
678 user_data->dlg.selected_clist = clist;
679 user_data->dlg.selected_row = row;
683 #ifdef USE_CONVERSATION_GRAPH
684 /****************************************************************************/
685 /* when the graph window gets destroyed */
686 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
688 /* note that graph window has been destroyed */
689 user_data->dlg.graph_window = NULL;
692 /****************************************************************************/
693 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
696 GtkCList *clist = NULL;
697 if (vp.fnumber != 0) {
698 clist = GTK_CLIST(user_data->dlg.clist_fwd);
699 row = gtk_clist_find_row_from_data(clist,
700 GUINT_TO_POINTER(vp.fnumber));
702 clist = GTK_CLIST(user_data->dlg.clist_rev);
703 row = gtk_clist_find_row_from_data(clist,
704 GUINT_TO_POINTER(vp.fnumber));
707 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
708 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
709 gtk_clist_select_row(clist, row, 0);
710 gtk_clist_moveto(clist, row, 0, 0.5, 0);
716 /****************************************************************************/
717 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
723 if (user_data->dlg.graph_window != NULL) {
724 /* There's already a graph window; reactivate it. */
725 reactivate_window(user_data->dlg.graph_window);
729 list = g_list_append(list, &(user_data->series_fwd));
730 list = g_list_append(list, &(user_data->series_rev));
732 user_data->series_fwd.color.pixel = 0;
733 user_data->series_fwd.color.red = 0x80ff;
734 user_data->series_fwd.color.green = 0xe0ff;
735 user_data->series_fwd.color.blue = 0xffff;
736 user_data->series_fwd.yvalue = 0.5;
738 user_data->series_rev.color.pixel = 0;
739 user_data->series_rev.color.red = 0x60ff;
740 user_data->series_rev.color.green = 0xc0ff;
741 user_data->series_rev.color.blue = 0xffff;
742 user_data->series_rev.yvalue = -0.5;
744 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
745 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
746 user_data->port_src_fwd,
747 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
748 user_data->port_dst_fwd,
749 user_data->ssrc_fwd);
751 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
752 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
753 user_data->port_src_rev,
754 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
755 user_data->port_dst_rev,
756 user_data->ssrc_rev);
758 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
759 &graph_selection_callback, user_data);
760 gtk_signal_connect(GTK_OBJECT(user_data->dlg.graph_window), "destroy",
761 GTK_SIGNAL_FUNC(on_destroy_graph), user_data);
763 #endif /*USE_CONVERSATION_GRAPH*/
766 /****************************************************************************/
767 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
771 if (user_data->dlg.selected_clist!=NULL) {
772 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
773 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
774 goto_frame(&cfile, fnumber);
779 static void draw_stat(user_data_t *user_data);
781 /****************************************************************************/
782 /* re-dissects all packets */
783 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
785 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
786 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
787 redissect_packets(&cfile);
788 draw_stat(user_data);
791 /****************************************************************************/
792 /* on_destroy is automatically called after that */
793 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
795 gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
796 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
799 /****************************************************************************/
800 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
805 if (user_data->dlg.selected_clist==NULL)
808 if (user_data->dlg.selected_row==-1)
809 user_data->dlg.selected_row = 0;
811 clist = user_data->dlg.selected_clist;
812 row = user_data->dlg.selected_row + 1;
814 while (gtk_clist_get_text(clist,row,5,&text)) {
815 if (strcmp(text, OK_TEXT) != 0) {
816 gtk_clist_select_row(clist, row, 0);
817 gtk_clist_moveto(clist, row, 0, 0.5, 0);
825 while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
826 if (strcmp(text, OK_TEXT) != 0) {
827 gtk_clist_select_row(clist, row, 0);
828 gtk_clist_moveto(clist, row, 0, 0.5, 0);
835 /****************************************************************************/
836 /* when we want to save the information */
837 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
840 GtkWidget *rev, *forw, *both;
841 user_data_t *user_data;
847 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
849 /* Perhaps the user specified a directory instead of a file.
850 Check whether they did. */
851 if (test_for_directory(g_dest) == EISDIR) {
852 /* It's a directory - set the file selection box to display it. */
853 set_last_open_dir(g_dest);
855 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
859 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
860 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
861 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
862 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
864 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
865 fp = fopen(g_dest, "w");
867 if (GTK_TOGGLE_BUTTON(both)->active) {
868 fprintf(fp, "Forward\n");
871 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
873 fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
875 fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
879 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
880 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
881 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
883 fprintf(fp,"%s",columnText);
885 fprintf(fp,",%s",columnText);
894 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
896 if (GTK_TOGGLE_BUTTON(both)->active) {
897 fp = fopen(g_dest, "a");
898 fprintf(fp, "\nReverse\n");
900 fp = fopen(g_dest, "w");
902 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
904 fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
906 fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
910 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
911 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
912 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
914 fprintf(fp,"%s",columnText);
916 fprintf(fp,",%s",columnText);
924 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
927 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
929 user_data->dlg.save_csv_as_w = NULL;
932 /* when the user wants to save the csv information in a file */
933 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
937 GtkWidget *label_format;
938 GtkWidget *channels_label;
939 GSList *channels_group = NULL;
940 GtkWidget *forward_rb;
941 GtkWidget *reversed_rb;
945 if (user_data->dlg.save_csv_as_w != NULL) {
946 /* There's already a Save CSV info dialog box; reactivate it. */
947 reactivate_window(user_data->dlg.save_csv_as_w);
951 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
952 gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_csv_as_w), "destroy",
953 GTK_SIGNAL_FUNC(save_csv_as_destroy_cb), user_data);
955 /* Container for each row of widgets */
956 vertb = gtk_vbox_new(FALSE, 0);
957 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
958 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
959 vertb, FALSE, FALSE, 0);
960 gtk_widget_show (vertb);
962 table1 = gtk_table_new (2, 4, FALSE);
963 gtk_widget_show (table1);
964 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
965 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
966 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
968 label_format = gtk_label_new ("Format: Comma Separated Values");
969 gtk_widget_show (label_format);
970 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
971 (GtkAttachOptions) (GTK_FILL),
972 (GtkAttachOptions) (0), 0, 0);
975 channels_label = gtk_label_new ("Channels:");
976 gtk_widget_show (channels_label);
977 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
978 (GtkAttachOptions) (GTK_FILL),
979 (GtkAttachOptions) (0), 0, 0);
980 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
982 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
983 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
984 gtk_widget_show (forward_rb);
985 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
986 (GtkAttachOptions) (GTK_FILL),
987 (GtkAttachOptions) (0), 0, 0);
989 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
990 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
991 gtk_widget_show (reversed_rb);
992 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
993 (GtkAttachOptions) (GTK_FILL),
994 (GtkAttachOptions) (0), 0, 0);
996 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
997 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
998 gtk_widget_show (both_rb);
999 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1000 (GtkAttachOptions) (GTK_FILL),
1001 (GtkAttachOptions) (0), 0, 0);
1003 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1005 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1006 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1007 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1008 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1009 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1011 /* Connect the cancel_button to destroy the widget */
1012 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1013 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1014 user_data->dlg.save_csv_as_w);
1016 /* Catch the "key_press_event" signal in the window, so that we can catch
1017 the ESC key being pressed and act as if the "Cancel" button had
1019 dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1021 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
1022 GTK_SIGNAL_FUNC(save_csv_as_ok_cb), user_data->dlg.save_csv_as_w);
1024 gtk_widget_show(user_data->dlg.save_csv_as_w);
1028 /****************************************************************************/
1029 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1031 /* Note that we no longer have a Save voice info dialog box. */
1032 user_data->dlg.save_voice_as_w = NULL;
1035 /****************************************************************************/
1036 /* here we save it into a file that user specified */
1037 /* XXX what about endians here? could go something wrong? */
1038 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1040 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1044 guint32 f_write_silence = 0;
1045 guint32 r_write_silence = 0;
1047 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1048 gboolean stop_flag = FALSE;
1050 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1053 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1059 /* open file for saving */
1060 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1067 progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag);
1069 /* First we write the .au header. XXX Hope this is endian independant */
1070 /* the magic word 0x2e736e64 == .snd */
1071 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1072 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1073 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1074 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1075 /* header offset == 24 bytes */
1076 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1077 write(to_fd, pd, 1);
1078 write(to_fd, pd, 1);
1079 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1080 /* total length, it is permited to set this to 0xffffffff */
1081 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1082 write(to_fd, pd, 1);
1083 write(to_fd, pd, 1);
1084 write(to_fd, pd, 1);
1085 /* encoding format == 8 bit ulaw */
1086 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1087 write(to_fd, pd, 1);
1088 write(to_fd, pd, 1);
1089 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1090 /* sample rate == 8000 Hz */
1091 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1092 write(to_fd, pd, 1);
1093 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1094 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1096 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1097 write(to_fd, pd, 1);
1098 write(to_fd, pd, 1);
1099 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1102 /* only forward direction */
1104 progbar_count = user_data->forward.saveinfo.count;
1105 progbar_quantum = user_data->forward.saveinfo.count/100;
1106 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1109 if((count > progbar_nextstep) && (count <= progbar_count)) {
1110 update_progress_dlg(progbar,
1111 (gfloat) count/progbar_count, "Saving");
1112 progbar_nextstep = progbar_nextstep + progbar_quantum;
1115 *pd = (unsigned char)linear2ulaw(f_pd);
1116 fwritten = write(to_fd, pd, 1);
1117 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1121 destroy_progress_dlg(progbar);
1127 /* only reversed direction */
1129 progbar_count = user_data->reversed.saveinfo.count;
1130 progbar_quantum = user_data->reversed.saveinfo.count/100;
1131 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1134 if((count > progbar_nextstep) && (count <= progbar_count)) {
1135 update_progress_dlg(progbar,
1136 (gfloat) count/progbar_count, "Saving");
1137 progbar_nextstep = progbar_nextstep + progbar_quantum;
1140 *pd = (unsigned char)linear2ulaw(r_pd);
1141 rwritten = write(to_fd, pd, 1);
1142 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1146 destroy_progress_dlg(progbar);
1152 /* both directions */
1154 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
1155 (progbar_count = user_data->forward.saveinfo.count) :
1156 (progbar_count = user_data->reversed.saveinfo.count);
1157 progbar_quantum = progbar_count/100;
1158 /* since conversation in one way can start later than in the other one,
1159 * we have to write some silence information for one channel */
1160 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1162 (user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000;
1164 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1166 (user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000;
1171 if((count > progbar_nextstep) && (count <= progbar_count)) {
1172 update_progress_dlg(progbar,
1173 (gfloat) count/progbar_count, "Saving");
1174 progbar_nextstep = progbar_nextstep + progbar_quantum;
1177 if(f_write_silence > 0) {
1178 rread = read(rev_fd, &r_pd, 2);
1183 else if(r_write_silence > 0) {
1184 fread = read(forw_fd, &f_pd, 2);
1190 fread = read(forw_fd, &f_pd, 2);
1191 rread = read(rev_fd, &r_pd, 2);
1193 if ((rread == 0) && (fread == 0))
1195 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1196 rwritten = write(to_fd, pd, 1);
1197 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1201 destroy_progress_dlg(progbar);
1207 destroy_progress_dlg(progbar);
1215 /****************************************************************************/
1216 /* the user wants to save in a file */
1217 /* XXX support for different formats is currently commented out */
1218 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1221 /*GtkWidget *wav, *au, *sw;*/
1222 GtkWidget *rev, *forw, *both;
1223 user_data_t *user_data;
1224 gint channels /*, format*/;
1226 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1228 /* Perhaps the user specified a directory instead of a file.
1229 Check whether they did. */
1230 if (test_for_directory(g_dest) == EISDIR) {
1231 /* It's a directory - set the file selection box to display it. */
1232 set_last_open_dir(g_dest);
1234 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1238 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1239 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1240 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1241 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1242 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1243 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1244 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1246 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1247 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1248 * disable the ok button or disable the buttons for direction if only one is not ok. The
1249 * problem is if we open the save voice dialog and then click the refresh button and maybe
1250 * the state changes, so we can't save anymore. In this case we should be able to update
1251 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1254 /* we can not save in both dirctions */
1255 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1256 /* there are many combinations here, we just exit when first matches */
1257 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
1258 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1259 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1260 "Can't save in a file: Unsupported codec!");
1261 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
1262 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1263 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1264 "Can't save in a file: Wrong length of captured packets!");
1265 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
1266 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1267 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1268 "Can't save in a file: RTP data with padding!");
1270 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1271 "Can't save in a file: File I/O problem!");
1274 /* we can not save forward direction */
1275 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1276 (GTK_TOGGLE_BUTTON (both)->active))) {
1277 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1278 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1279 "Can't save forward direction in a file: Unsupported codec!");
1280 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1281 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1282 "Can't save forward direction in a file: Wrong length of captured packets!");
1283 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1284 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1285 "Can't save forward direction in a file: RTP data with padding!");
1287 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1288 "Can't save forward direction in a file: File I/O problem!");
1291 /* we can not save reversed direction */
1292 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1293 (GTK_TOGGLE_BUTTON (both)->active))) {
1294 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1295 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1296 "Can't save reversed direction in a file: Unsupported codec!");
1297 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1298 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1299 "Can't save reversed direction in a file: Wrong length of captured packets!");
1300 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1301 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1302 "Can't save reversed direction in a file: RTP data with padding!");
1303 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1304 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1305 "Can't save reversed direction in a file: No RTP data!");
1307 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1308 "Can't save reversed direction in a file: File I/O problem!");
1312 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1314 else if (GTK_TOGGLE_BUTTON (au)->active)
1316 else if (GTK_TOGGLE_BUTTON (sw)->active)
1319 if (GTK_TOGGLE_BUTTON (rev)->active)
1321 else if (GTK_TOGGLE_BUTTON (both)->active)
1326 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1327 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1328 "An error occured while saving voice in a file!");
1332 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1335 /****************************************************************************/
1336 /* when the user wants to save the voice information in a file */
1337 /* XXX support for different formats is currently commented out */
1338 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1342 GtkWidget *label_format;
1343 GtkWidget *channels_label;
1344 /*GSList *format_group = NULL;*/
1345 GSList *channels_group = NULL;
1346 GtkWidget *forward_rb;
1347 GtkWidget *reversed_rb;
1349 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1352 /* if we can't save in a file: wrong codec, cut packets or other errors */
1353 /* shold the error arise here or later when you click ok button ?
1354 * if we do it here, then we must disable the refresh button, so we don't do it here */
1356 if (user_data->dlg.save_voice_as_w != NULL) {
1357 /* There's already a Save voice info dialog box; reactivate it. */
1358 reactivate_window(user_data->dlg.save_voice_as_w);
1362 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1363 gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_voice_as_w), "destroy",
1364 GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), user_data);
1366 /* Container for each row of widgets */
1367 vertb = gtk_vbox_new(FALSE, 0);
1368 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1369 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1370 vertb, FALSE, FALSE, 0);
1371 gtk_widget_show (vertb);
1373 table1 = gtk_table_new (2, 4, FALSE);
1374 gtk_widget_show (table1);
1375 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1376 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1377 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1379 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1380 gtk_widget_show (label_format);
1381 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1382 (GtkAttachOptions) (GTK_FILL),
1383 (GtkAttachOptions) (0), 0, 0);
1385 /* we support .au - ulaw*/
1386 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1387 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1388 gtk_widget_show (wav_rb);
1389 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1390 (GtkAttachOptions) (GTK_FILL),
1391 (GtkAttachOptions) (0), 0, 0);
1393 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1394 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1395 gtk_widget_show (sw_rb);
1396 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1397 (GtkAttachOptions) (GTK_FILL),
1398 (GtkAttachOptions) (0), 0, 0);
1399 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1400 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1401 gtk_widget_show (au_rb);
1402 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1403 (GtkAttachOptions) (GTK_FILL),
1404 (GtkAttachOptions) (0), 0, 0);
1407 channels_label = gtk_label_new ("Channels:");
1408 gtk_widget_show (channels_label);
1409 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1410 (GtkAttachOptions) (GTK_FILL),
1411 (GtkAttachOptions) (0), 0, 0);
1412 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1414 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1415 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1416 gtk_widget_show (forward_rb);
1417 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1418 (GtkAttachOptions) (GTK_FILL),
1419 (GtkAttachOptions) (0), 0, 0);
1421 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1422 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1423 gtk_widget_show (reversed_rb);
1424 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1425 (GtkAttachOptions) (GTK_FILL),
1426 (GtkAttachOptions) (0), 0, 0);
1428 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1429 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1430 gtk_widget_show (both_rb);
1431 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1432 (GtkAttachOptions) (GTK_FILL),
1433 (GtkAttachOptions) (0), 0, 0);
1435 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1437 /* if one direction is nok we don't allow saving
1438 XXX this is not ok since the user can click the refresh button and cause changes
1439 but we can not update this window. So we move all the decision on the time the ok
1441 if (user_data->forward.saved == FALSE) {
1442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1443 gtk_widget_set_sensitive(forward_rb, FALSE);
1444 gtk_widget_set_sensitive(both_rb, FALSE);
1446 else if (user_data->reversed.saved == FALSE) {
1447 gtk_widget_set_sensitive(reversed_rb, FALSE);
1448 gtk_widget_set_sensitive(both_rb, FALSE);
1452 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1453 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1454 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1455 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1456 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1457 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1458 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1459 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1461 /* Connect the cancel_button to destroy the widget */
1462 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1463 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1464 user_data->dlg.save_voice_as_w);
1466 /* Catch the "key_press_event" signal in the window, so that we can catch
1467 the ESC key being pressed and act as if the "Cancel" button had
1469 dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1471 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
1472 GTK_SIGNAL_FUNC(save_voice_as_ok_cb), user_data->dlg.save_voice_as_w);
1474 gtk_widget_show(user_data->dlg.save_voice_as_w);
1478 /****************************************************************************/
1479 /* when we are finished with redisection, we add the label for the statistic */
1480 static void draw_stat(user_data_t *user_data)
1482 gchar label_max[200];
1483 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1484 - user_data->forward.statinfo.start_seq_nr + 1;
1485 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1486 - user_data->reversed.statinfo.start_seq_nr + 1;
1487 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1488 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1490 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
1491 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1492 " Sequence errors = %u",
1493 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr, user_data->forward.statinfo.total_nr,
1494 f_expected, f_lost, user_data->forward.statinfo.sequence);
1496 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1498 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
1499 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1500 " Sequence errors = %u",
1501 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr, user_data->reversed.statinfo.total_nr,
1502 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1504 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1509 /****************************************************************************/
1510 /* append a line to clist */
1511 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1512 double delay, double jitter, gchar *status, gboolean marker,
1513 gchar *timeStr, guint32 pkt_len, GdkColor *color)
1519 data[0]=&field[0][0];
1520 data[1]=&field[1][0];
1521 data[2]=&field[2][0];
1522 data[3]=&field[3][0];
1523 data[4]=&field[4][0];
1524 data[5]=&field[5][0];
1525 data[6]=&field[6][0];
1526 data[7]=&field[7][0];
1528 g_snprintf(field[0], 20, "%u", number);
1529 g_snprintf(field[1], 20, "%u", seq_num);
1530 g_snprintf(field[2], 20, "%f", delay);
1531 g_snprintf(field[3], 20, "%f", jitter);
1532 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1533 g_snprintf(field[5], 40, "%s", status);
1534 g_snprintf(field[6], 32, "%s", timeStr);
1535 g_snprintf(field[7], 20, "%u", pkt_len);
1537 added_row = gtk_clist_append(GTK_CLIST(clist), data);
1538 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1539 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1542 /****************************************************************************/
1543 /* Create the dialog box with all widgets */
1544 void create_rtp_dialog(user_data_t* user_data)
1546 GtkWidget *window = NULL;
1547 GtkWidget *clist_fwd;
1548 GtkWidget *clist_rev;
1549 GtkWidget *label_stats_fwd;
1550 GtkWidget *label_stats_rev;
1551 GtkWidget *notebook;
1553 GtkWidget *main_vb, *page, *page_r, *label, *label1, *label2, *label3;
1554 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1555 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1556 #ifdef USE_CONVERSATION_GRAPH
1557 GtkWidget *graph_bt;
1560 gchar *titles[8] = {"Packet", "Sequence", "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"};
1561 gchar label_forward[150];
1562 gchar label_reverse[150];
1564 gchar str_ip_src[16];
1565 gchar str_ip_dst[16];
1568 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1569 gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1570 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1571 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1572 GTK_SIGNAL_FUNC(on_destroy), user_data);
1574 /* Container for each row of widgets */
1575 main_vb = gtk_vbox_new(FALSE, 3);
1576 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1577 gtk_container_add(GTK_CONTAINER(window), main_vb);
1578 gtk_widget_show(main_vb);
1582 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1583 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1585 g_snprintf(label_forward, 149,
1586 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1587 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1589 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1590 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1592 g_snprintf(label_reverse, 149,
1593 "Analysing connection from %s port %u to %s port %u SSRC = %u\n",
1594 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1596 /* Start a notebook for flipping between sets of changes */
1597 notebook = gtk_notebook_new();
1598 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1599 gtk_object_set_data(GTK_OBJECT(window), "notebook", notebook);
1600 gtk_signal_connect(GTK_OBJECT (notebook), "switch_page",
1601 GTK_SIGNAL_FUNC (on_notebook_switch_page),
1604 /* page for forward connection */
1605 page = gtk_vbox_new(FALSE, 5);
1606 gtk_container_set_border_width(GTK_CONTAINER(page), 20);
1608 /* scrolled window */
1609 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1610 gtk_widget_set_usize(scrolled_window, 600, 200);
1611 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1612 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1614 /* direction label */
1615 label1 = gtk_label_new(label_forward);
1616 gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
1618 /* place for some statistics */
1619 label_stats_fwd = gtk_label_new("\n\n");
1620 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 5);
1622 /* clist for the information */
1623 clist_fwd = gtk_clist_new_with_titles(8, titles);
1624 gtk_widget_show(clist_fwd);
1625 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1626 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1627 gtk_signal_connect(GTK_OBJECT (clist_fwd), "select_row",
1628 GTK_SIGNAL_FUNC (on_clist_select_row),
1630 /* Hide date and length column */
1631 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1632 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1635 label = gtk_label_new(" Forward Direction ");
1636 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1638 /* column width and justification */
1639 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 80);
1640 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 80);
1641 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 80);
1642 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 80);
1643 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 40);
1644 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_CENTER);
1645 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_CENTER);
1646 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1647 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1648 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1649 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1651 /* same page for reversed connection */
1652 page_r = gtk_vbox_new(FALSE, 5);
1653 gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
1654 scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
1655 gtk_widget_set_usize(scrolled_window_r, 600, 200);
1656 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
1657 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1658 label3 = gtk_label_new(label_reverse);
1659 gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
1660 label_stats_rev = gtk_label_new("\n\n");
1661 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 5);
1662 clist_rev = gtk_clist_new_with_titles(8, titles);
1663 gtk_widget_show(clist_rev);
1664 gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 6, FALSE);
1665 gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 7, FALSE);
1667 gtk_signal_connect(GTK_OBJECT (clist_rev), "select_row",
1668 GTK_SIGNAL_FUNC (on_clist_select_row),
1671 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
1672 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
1673 label2 = gtk_label_new(" Reversed Direction ");
1674 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
1676 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 0, 80);
1677 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 1, 80);
1678 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 2, 80);
1679 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 3, 80);
1680 gtk_clist_set_column_width(GTK_CLIST(clist_rev), 4, 40);
1681 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 0, GTK_JUSTIFY_CENTER);
1682 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 1, GTK_JUSTIFY_CENTER);
1683 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 2, GTK_JUSTIFY_CENTER);
1684 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 3, GTK_JUSTIFY_CENTER);
1685 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 4, GTK_JUSTIFY_CENTER);
1686 gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 5, GTK_JUSTIFY_CENTER);
1688 /* page for help&about or future
1689 page_help = gtk_hbox_new(FALSE, 5);
1690 label4 = gtk_label_new(" Future ");
1691 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
1692 frame = gtk_frame_new("");
1693 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
1694 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
1695 gtk_container_add(GTK_CONTAINER(frame), text);
1696 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
1697 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
1700 /* show all notebooks */
1701 gtk_widget_show_all(notebook);
1704 box4 = gtk_hbutton_box_new();
1705 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
1706 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
1707 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
1708 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
1709 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
1710 gtk_widget_show(box4);
1712 voice_bt = gtk_button_new_with_label("Save payload...");
1713 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
1714 gtk_widget_show(voice_bt);
1715 gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked",
1716 GTK_SIGNAL_FUNC(on_save_bt_clicked), user_data);
1718 csv_bt = gtk_button_new_with_label("Save as CSV...");
1719 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
1720 gtk_widget_show(csv_bt);
1721 gtk_signal_connect(GTK_OBJECT(csv_bt), "clicked",
1722 GTK_SIGNAL_FUNC(save_csv_as_cb), user_data);
1724 refresh_bt = gtk_button_new_with_label("Refresh");
1725 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
1726 gtk_widget_show(refresh_bt);
1727 gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked",
1728 GTK_SIGNAL_FUNC(on_refresh_bt_clicked), user_data);
1730 goto_bt = gtk_button_new_with_label("Go to frame");
1731 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
1732 gtk_widget_show(goto_bt);
1733 gtk_signal_connect(GTK_OBJECT(goto_bt), "clicked",
1734 GTK_SIGNAL_FUNC(on_goto_bt_clicked), user_data);
1736 #ifdef USE_CONVERSATION_GRAPH
1737 graph_bt = gtk_button_new_with_label("Graph");
1738 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
1739 gtk_widget_show(graph_bt);
1740 gtk_signal_connect(GTK_OBJECT(graph_bt), "clicked",
1741 GTK_SIGNAL_FUNC(on_graph_bt_clicked), user_data);
1744 next_bt = gtk_button_new_with_label("Next");
1745 gtk_container_add(GTK_CONTAINER(box4), next_bt);
1746 gtk_widget_show(next_bt);
1747 gtk_signal_connect(GTK_OBJECT(next_bt), "clicked",
1748 GTK_SIGNAL_FUNC(on_next_bt_clicked), user_data);
1750 close_bt = gtk_button_new_with_label("Close");
1751 gtk_container_add(GTK_CONTAINER(box4), close_bt);
1752 gtk_widget_show(close_bt);
1753 gtk_signal_connect(GTK_OBJECT(close_bt), "clicked",
1754 GTK_SIGNAL_FUNC(on_close_bt_clicked), user_data);
1756 gtk_widget_show(window);
1758 user_data->dlg.window = window;
1759 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
1760 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
1761 user_data->dlg.label_stats_fwd = label_stats_fwd;
1762 user_data->dlg.label_stats_rev = label_stats_rev;
1763 user_data->dlg.notebook = notebook;
1764 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
1765 user_data->dlg.selected_row = 0;
1769 /****************************************************************************/
1770 static gboolean process_node(proto_item *ptree_node, header_field_info *hfinformation,
1771 const gchar* proto_field, guint32* p_result)
1774 proto_item *proto_sibling_node;
1775 header_field_info *hfssrc;
1778 finfo = PITEM_FINFO(ptree_node);
1780 if (hfinformation==(finfo->hfinfo)) {
1781 hfssrc = proto_registrar_get_byname((gchar*) proto_field);
1784 for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL;
1785 ptree_node=g_node_next_sibling(ptree_node)) {
1786 finfo=PITEM_FINFO(ptree_node);
1787 if (hfssrc==finfo->hfinfo) {
1788 if (hfinformation->type==FT_IPv4) {
1789 ipv4 = fvalue_get(finfo->value);
1790 *p_result = ipv4_get_net_order_addr(ipv4);
1793 *p_result = fvalue_get_integer(finfo->value);
1800 proto_sibling_node = g_node_next_sibling(ptree_node);
1802 if (proto_sibling_node) {
1803 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
1809 /****************************************************************************/
1810 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
1811 const gchar* proto_name,
1812 const gchar* proto_field,
1815 proto_item *ptree_node;
1816 header_field_info *hfinformation;
1818 hfinformation = proto_registrar_get_byname((gchar*) proto_name);
1819 if (hfinformation == NULL)
1822 ptree_node = g_node_first_child(protocol_tree);
1826 return process_node(ptree_node, hfinformation, proto_field, p_result);
1830 /****************************************************************************/
1831 /* XXX only handles RTP over IPv4, should add IPv6 support */
1834 guint16 port_src_fwd,
1836 guint16 port_dst_fwd,
1839 guint16 port_src_rev,
1841 guint16 port_dst_rev,
1845 user_data_t *user_data;
1846 gchar filter_text[256];
1848 GString *error_string;
1850 user_data = g_malloc(sizeof(user_data_t));
1852 user_data->ip_src_fwd = ip_src_fwd;
1853 user_data->port_src_fwd = port_src_fwd;
1854 user_data->ip_dst_fwd = ip_dst_fwd;
1855 user_data->port_dst_fwd = port_dst_fwd;
1856 user_data->ssrc_fwd = ssrc_fwd;
1857 user_data->ip_src_rev = ip_src_rev;
1858 user_data->port_src_rev = port_src_rev;
1859 user_data->ip_dst_rev = ip_dst_rev;
1860 user_data->port_dst_rev = port_dst_rev;
1861 user_data->ssrc_rev = ssrc_rev;
1863 create_rtp_dialog(user_data);
1865 /* Try to compile the filter. */
1866 strcpy(filter_text,"rtp && ip");
1867 if (!dfilter_compile(filter_text, &sfcode)) {
1868 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
1872 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 ))",
1873 ip_to_str((ip_addr_p)&ip_src_fwd),
1875 ip_to_str((ip_addr_p)&ip_dst_fwd),
1877 ip_to_str((ip_addr_p)&ip_src_rev),
1879 ip_to_str((ip_addr_p)&ip_dst_rev),
1883 error_string = register_tap_listener("rtp", user_data, filter_text,
1884 (void*)rtp_reset, (void*)rtp_packet, (void*)rtp_draw);
1885 if (error_string != NULL) {
1886 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
1887 g_string_free(error_string, TRUE);
1893 /* file names for storing sound data */
1894 strncpy(user_data->f_tempname, "f_tempnameXXXXXX", TMPNAMSIZE);
1895 strncpy(user_data->r_tempname, "r_tempnameXXXXXX", TMPNAMSIZE);
1896 mkstemp(user_data->f_tempname);
1897 mkstemp(user_data->r_tempname);
1898 user_data->forward.saveinfo.fp = NULL;
1899 user_data->reversed.saveinfo.fp = NULL;
1900 user_data->dlg.save_voice_as_w = NULL;
1901 user_data->dlg.save_csv_as_w = NULL;
1902 #ifdef USE_CONVERSATION_GRAPH
1903 user_data->dlg.graph_window = NULL;
1904 user_data->series_fwd.value_pairs = NULL;
1905 user_data->series_rev.value_pairs = NULL;
1908 redissect_packets(&cfile);
1910 draw_stat(user_data);
1913 /****************************************************************************/
1914 /* entry point from main menu */
1915 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
1918 guint16 port_src_fwd;
1920 guint16 port_dst_fwd;
1921 guint32 ssrc_fwd = 0;
1923 guint16 port_src_rev;
1925 guint16 port_dst_rev;
1926 guint32 ssrc_rev = 0;
1928 gchar filter_text[256];
1931 epan_dissect_t *edt;
1933 gboolean frame_matched;
1935 GList *strinfo_list;
1936 GList *filtered_list = NULL;
1937 rtp_stream_info_t *strinfo;
1940 /* Try to compile the filter. */
1941 strcpy(filter_text,"rtp && ip");
1942 if (!dfilter_compile(filter_text, &sfcode)) {
1943 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
1946 /* we load the current file into cf variable */
1948 fdata = cf->current_frame;
1950 /* we are on the selected frame now */
1952 return; /* if we exit here it's an error */
1954 /* dissect the current frame */
1955 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) {
1956 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1957 file_read_error_message(err), cf->filename);
1960 edt = epan_dissect_new(TRUE, FALSE);
1961 epan_dissect_prime_dfilter(edt, sfcode);
1962 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
1963 frame_matched = dfilter_apply_edt(sfcode, edt);
1965 /* if it is not an rtp frame, show the rtpstream dialog */
1966 frame_matched = dfilter_apply_edt(sfcode, edt);
1967 if (frame_matched != 1) {
1968 rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
1971 epan_dissect_free(edt);
1972 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!");
1977 /* ok, it is a RTP frame, so let's get the ip and port values */
1978 g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
1979 g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
1980 port_src_fwd = edt->pi.srcport;
1981 port_dst_fwd = edt->pi.destport;
1983 /* assume the inverse ip/port combination for the reverse direction */
1984 g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
1985 g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
1986 port_src_rev = edt->pi.destport;
1987 port_dst_rev = edt->pi.srcport;
1989 /* now we need the SSRC value of the current frame */
1990 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
1991 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!");
1995 /* search for reversed direction in the global rtp streams list */
1997 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
1998 while (strinfo_list)
2000 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
2001 if (strinfo->src_addr==ip_src_fwd
2002 && strinfo->src_port==port_src_fwd
2003 && strinfo->dest_addr==ip_dst_fwd
2004 && strinfo->dest_port==port_dst_fwd)
2006 filtered_list = g_list_prepend(filtered_list, strinfo);
2009 if (strinfo->src_addr==ip_src_rev
2010 && strinfo->src_port==port_src_rev
2011 && strinfo->dest_addr==ip_dst_rev
2012 && strinfo->dest_port==port_dst_rev)
2015 filtered_list = g_list_append(filtered_list, strinfo);
2017 ssrc_rev = strinfo->ssrc;
2020 strinfo_list = g_list_next(strinfo_list);
2023 /* if more than one reverse streams found, we let the user choose the right one */
2025 rtpstream_dlg_show(filtered_list);
2044 /****************************************************************************/
2046 rtp_analysis_init(char *dummy _U_)
2048 rtp_analysis_cb(NULL, NULL);
2051 /****************************************************************************/
2053 register_tap_listener_rtp_analysis(void)
2055 register_ethereal_tap("rtp", rtp_analysis_init);
2059 register_tap_menu_rtp_analysis(void)
2061 register_tap_menu_item("Statistics/RTP Streams/Analyse...",
2062 rtp_analysis_cb, NULL, NULL);