2 * RTP analysis addition for ethereal
4 * $Id: rtp_analysis.c,v 1.26 2004/01/25 02:14:05 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"
68 #include "image/clist_ascend.xpm"
69 #include "image/clist_descend.xpm"
80 #include <io.h> /* open/close on win32 */
87 /****************************************************************************/
89 typedef struct column_arrows {
92 GtkWidget *descend_pm;
95 typedef struct _dialog_data_t {
99 GtkWidget *label_stats_fwd;
100 GtkWidget *label_stats_rev;
101 column_arrows *col_arrows_fwd;
102 column_arrows *col_arrows_rev;
104 GtkCList *selected_clist;
105 GtkWidget *save_voice_as_w;
106 GtkWidget *save_csv_as_w;
108 #ifdef USE_CONVERSATION_GRAPH
109 GtkWidget *graph_window;
113 #define OK_TEXT "[ Ok ]"
114 #define PT_UNDEFINED -1
116 /* type of error when saving voice in a file didn't succeed */
119 TAP_RTP_WRONG_LENGTH,
120 TAP_RTP_PADDING_ERROR,
121 TAP_RTP_FILE_OPEN_ERROR,
126 /****************************************************************************/
127 /* structure that holds the information about the forward and reversed direction */
128 typedef struct _tap_rtp_stat_t {
129 gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
130 /* use (flags & STAT_FLAG_FIRST) instead */
131 /* all of the following fields will be initialized after
132 rtp_packet_analyse has been called */
133 guint32 flags; /* see STAT_FLAG-defines below */
136 guint32 delta_timestamp;
143 guint16 start_seq_nr;
153 /* status flags for the flags parameter in tap_rtp_stat_t */
154 #define STAT_FLAG_FIRST 0x01
155 #define STAT_FLAG_MARKER 0x02
156 #define STAT_FLAG_WRONG_SEQ 0x04
157 #define STAT_FLAG_PT_CHANGE 0x08
158 #define STAT_FLAG_PT_CN 0x10
159 #define STAT_FLAG_FOLLOW_PT_CN 0x20
160 #define STAT_FLAG_REG_PT_CHANGE 0x40
162 typedef struct _tap_rtp_save_info_t {
165 error_type_t error_type;
167 } tap_rtp_save_info_t;
170 /* structure that holds the information about the forward and reversed direction */
171 struct _info_direction {
172 tap_rtp_stat_t statinfo;
173 tap_rtp_save_info_t saveinfo;
176 #define TMPNAMSIZE 100
178 /* structure that holds general information about the connection
179 * and structures for both directions */
180 typedef struct _user_data_t {
181 /* tap associated data*/
183 guint16 port_src_fwd;
185 guint16 port_dst_fwd;
188 guint16 port_src_rev;
190 guint16 port_dst_rev;
193 struct _info_direction forward;
194 struct _info_direction reversed;
196 char f_tempname[TMPNAMSIZE];
197 char r_tempname[TMPNAMSIZE];
199 /* dialog associated data */
202 #ifdef USE_CONVERSATION_GRAPH
203 time_series_t series_fwd;
204 time_series_t series_rev;
210 static gchar *titles[8] = {
222 typedef const guint8 * ip_addr_p;
225 /****************************************************************************/
228 /****************************************************************************/
229 /* when there is a [re]reading of packet's */
231 rtp_reset(void *user_data_arg)
233 user_data_t *user_data = user_data_arg;
235 user_data->forward.statinfo.first_packet = TRUE;
236 user_data->reversed.statinfo.first_packet = TRUE;
237 user_data->forward.statinfo.max_delay = 0;
238 user_data->reversed.statinfo.max_delay = 0;
239 user_data->forward.statinfo.delay = 0;
240 user_data->reversed.statinfo.delay = 0;
241 user_data->forward.statinfo.jitter = 0;
242 user_data->reversed.statinfo.jitter = 0;
243 user_data->forward.statinfo.timestamp = 0;
244 user_data->reversed.statinfo.timestamp = 0;
245 user_data->forward.statinfo.max_nr = 0;
246 user_data->reversed.statinfo.max_nr = 0;
247 user_data->forward.statinfo.total_nr = 0;
248 user_data->reversed.statinfo.total_nr = 0;
249 user_data->forward.statinfo.sequence = 0;
250 user_data->reversed.statinfo.sequence = 0;
251 user_data->forward.statinfo.start_seq_nr = 0;
252 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
253 user_data->forward.statinfo.stop_seq_nr = 0;
254 user_data->reversed.statinfo.stop_seq_nr = 0;
255 user_data->forward.statinfo.cycles = 0;
256 user_data->reversed.statinfo.cycles = 0;
257 user_data->forward.statinfo.under = FALSE;
258 user_data->reversed.statinfo.under = FALSE;
259 user_data->forward.statinfo.start_time = 0;
260 user_data->reversed.statinfo.start_time = 0;
261 user_data->forward.statinfo.time = 0;
262 user_data->reversed.statinfo.time = 0;
263 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
264 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
266 user_data->forward.saveinfo.count = 0;
267 user_data->reversed.saveinfo.count = 0;
268 user_data->forward.saveinfo.saved = FALSE;
269 user_data->reversed.saveinfo.saved = FALSE;
271 #ifdef USE_CONVERSATION_GRAPH
272 if (user_data->dlg.graph_window != NULL)
273 gtk_widget_destroy(user_data->dlg.graph_window);
275 g_array_free(user_data->series_fwd.value_pairs, TRUE);
276 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
278 g_array_free(user_data->series_rev.value_pairs, TRUE);
279 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
282 /* XXX check for error at fclose? */
283 if (user_data->forward.saveinfo.fp != NULL)
284 fclose(user_data->forward.saveinfo.fp);
285 if (user_data->reversed.saveinfo.fp != NULL)
286 fclose(user_data->reversed.saveinfo.fp);
287 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
288 if (user_data->forward.saveinfo.fp == NULL)
289 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
290 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
291 if (user_data->reversed.saveinfo.fp == NULL)
292 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
296 /****************************************************************************/
297 /* here we can redraw the output */
299 static void rtp_draw(void *prs _U_)
304 /* forward declarations */
305 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
306 double delay, double jitter, gchar *status, gboolean marker,
307 gchar *timeStr, guint32 pkt_len, GdkColor *color);
309 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
310 packet_info *pinfo, struct _rtp_info *rtpinfo);
311 static int rtp_packet_add_info(GtkCList *clist,
312 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
313 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
314 tap_rtp_stat_t *statinfo,
315 packet_info *pinfo, struct _rtp_info *rtpinfo);
318 /****************************************************************************/
319 /* whenever a RTP packet is seen by the tap listener */
320 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *rtpinfo_arg)
322 user_data_t *user_data = user_data_arg;
323 struct _rtp_info *rtpinfo = rtpinfo_arg;
324 #ifdef USE_CONVERSATION_GRAPH
328 /* we ignore packets that are not displayed */
329 if (pinfo->fd->flags.passed_dfilter == 0)
332 /* is it the forward direction? */
333 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
334 #ifdef USE_CONVERSATION_GRAPH
335 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
336 vp.fnumber = pinfo->fd->num;
337 g_array_append_val(user_data->series_fwd.value_pairs, vp);
339 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
340 rtp_packet_add_info(user_data->dlg.clist_fwd,
341 &(user_data->forward.statinfo), pinfo, rtpinfo);
342 rtp_packet_save_payload(&(user_data->forward.saveinfo),
343 &(user_data->forward.statinfo), pinfo, rtpinfo);
345 /* is it the reversed direction? */
346 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
347 #ifdef USE_CONVERSATION_GRAPH
348 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
349 vp.fnumber = pinfo->fd->num;
350 g_array_append_val(user_data->series_rev.value_pairs, vp);
352 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
353 rtp_packet_add_info(user_data->dlg.clist_rev,
354 &(user_data->reversed.statinfo), pinfo, rtpinfo);
355 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
356 &(user_data->reversed.statinfo), pinfo, rtpinfo);
363 /****************************************************************************/
364 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
365 packet_info *pinfo, struct _rtp_info *rtpinfo)
368 double current_jitter;
372 /* check payload type */
373 if (rtpinfo->info_payload_type == PT_CN
374 || rtpinfo->info_payload_type == PT_CN_OLD)
375 statinfo->flags |= STAT_FLAG_PT_CN;
376 if (statinfo->pt == PT_CN
377 || statinfo->pt == PT_CN_OLD)
378 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
379 if (rtpinfo->info_payload_type != statinfo->pt)
380 statinfo->flags |= STAT_FLAG_PT_CHANGE;
382 statinfo->pt = rtpinfo->info_payload_type;
384 /* store the current time and calculate the current jitter */
385 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
386 current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
387 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
388 statinfo->delay = current_time-(statinfo->time);
389 statinfo->jitter = current_jitter;
391 /* is this the first packet we got in this direction? */
392 if (statinfo->first_packet) {
393 statinfo->start_seq_nr = rtpinfo->info_seq_num;
394 statinfo->start_time = current_time;
396 statinfo->jitter = 0;
397 statinfo->flags |= STAT_FLAG_FIRST;
398 statinfo->first_packet = FALSE;
400 /* is it a packet with the mark bit set? */
401 if (rtpinfo->info_marker_set) {
402 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
403 statinfo->flags |= STAT_FLAG_MARKER;
405 /* is it a regular packet? */
406 if (!(statinfo->flags & STAT_FLAG_FIRST)
407 && !(statinfo->flags & STAT_FLAG_MARKER)
408 && !(statinfo->flags & STAT_FLAG_PT_CN)
409 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
410 /* include it in maximum delay calculation */
411 if (statinfo->delay > statinfo->max_delay) {
412 statinfo->max_delay = statinfo->delay;
413 statinfo->max_nr = pinfo->fd->num;
416 /* regular payload change? (CN ignored) */
417 if (!(statinfo->flags & STAT_FLAG_FIRST)
418 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
419 if ((statinfo->pt != statinfo->reg_pt)
420 && (statinfo->reg_pt != PT_UNDEFINED)) {
421 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
425 /* set regular payload*/
426 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
427 statinfo->reg_pt = statinfo->pt;
431 /* When calculating expected rtp packets the seq number can wrap around
432 * so we have to count the number of cycles
433 * Variable cycles counts the wraps around in forwarding connection and
434 * under is flag that indicates where we are
436 * XXX how to determine number of cycles with all possible lost, late
437 * and duplicated packets without any doubt? It seems to me, that
438 * because of all possible combination of late, duplicated or lost
439 * packets, this can only be more or less good approximation
441 * There are some combinations (rare but theoretically possible),
442 * where below code won't work correctly - statistic may be wrong then.
445 /* so if the current sequence number is less than the start one
446 * we assume, that there is another cycle running */
447 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
449 statinfo->under = TRUE;
451 /* what if the start seq nr was 0? Then the above condition will never
452 * be true, so we add another condition. XXX The problem would arise
453 * if one of the packets with seq nr 0 or 65535 would be lost or late */
454 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
455 (statinfo->under == FALSE)){
457 statinfo->under = TRUE;
459 /* the whole round is over, so reset the flag */
460 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
461 statinfo->under = FALSE;
464 /* Since it is difficult to count lost, duplicate or late packets separately,
465 * we would like to know at least how many times the sequence number was not ok */
467 /* if the current seq number equals the last one or if we are here for
468 * the first time, then it is ok, we just store the current one as the last one */
469 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
470 statinfo->seq_num = rtpinfo->info_seq_num;
471 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
472 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
473 statinfo->seq_num = rtpinfo->info_seq_num;
475 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
476 statinfo->seq_num = rtpinfo->info_seq_num;
477 statinfo->sequence++;
478 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
480 /* late or duplicated */
481 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
482 statinfo->sequence++;
483 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
486 statinfo->time = current_time;
487 statinfo->timestamp = rtpinfo->info_timestamp;
488 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
489 statinfo->total_nr++;
495 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
496 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
497 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
498 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
500 /****************************************************************************/
501 /* adds statistics information from the packet to the clist */
502 static int rtp_packet_add_info(GtkCList *clist,
503 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
510 GdkColor color = COLOR_DEFAULT;
512 then = pinfo->fd->abs_secs;
513 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
514 tm_tmp = localtime(&then);
515 snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
518 tm_tmp->tm_year + 1900,
524 if (statinfo->pt == PT_CN) {
525 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
528 else if (statinfo->pt == PT_CN_OLD) {
529 snprintf(status,40,"Comfort noise (PT=19, reserved)");
532 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
533 snprintf(status,40,"Wrong sequence nr.");
537 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
538 && !(statinfo->flags & STAT_FLAG_FIRST)
539 && !(statinfo->flags & STAT_FLAG_PT_CN)
540 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
542 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
543 snprintf(status,40,"Payload changed to PT=%u", statinfo->pt);
544 color = COLOR_WARNING;
546 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
547 && !(statinfo->flags & STAT_FLAG_FIRST)
548 && !(statinfo->flags & STAT_FLAG_PT_CN)
549 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
550 && !(statinfo->flags & STAT_FLAG_MARKER)) {
551 snprintf(status,40,"Marker missing?");
552 color = COLOR_WARNING;
555 if (statinfo->flags & STAT_FLAG_MARKER) {
556 color = COLOR_WARNING;
558 snprintf(status,40,OK_TEXT);
561 /* is this the first packet we got in this direction? */
562 if (statinfo->flags & STAT_FLAG_FIRST) {
564 pinfo->fd->num, rtpinfo->info_seq_num,
568 rtpinfo->info_marker_set,
569 timeStr, pinfo->fd->pkt_len,
574 pinfo->fd->num, rtpinfo->info_seq_num,
578 rtpinfo->info_marker_set,
579 timeStr, pinfo->fd->pkt_len,
587 /****************************************************************************/
588 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
589 tap_rtp_stat_t *statinfo,
590 packet_info *pinfo, struct _rtp_info *rtpinfo)
596 /* is this the first packet we got in this direction? */
597 if (statinfo->flags & STAT_FLAG_FIRST) {
598 if (saveinfo->fp == NULL) {
599 saveinfo->saved = FALSE;
600 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
603 saveinfo->saved = TRUE;
606 /* save the voice information */
607 /* if there was already an error, we quit */
608 if (saveinfo->saved == FALSE)
611 /* if the captured length and packet length aren't equal, we quit
612 * because there is some information missing */
613 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
614 saveinfo->saved = FALSE;
615 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
619 /* if padding bit is set, but the padding count is bigger
620 * then the whole RTP data - error with padding count */
621 if ( (rtpinfo->info_padding_set != FALSE) &&
622 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
623 saveinfo->saved = FALSE;
624 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
628 /* do we need to insert some silence? */
629 if ((rtpinfo->info_marker_set) &&
630 !(statinfo->flags & STAT_FLAG_FIRST) &&
631 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
632 /* the amount of silence should be the difference between
633 * the last timestamp and the current one minus x
634 * x should equal the amount of information in the last frame
635 * XXX not done yet */
636 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
637 rtpinfo->info_padding_count); i++) {
638 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
639 fwrite(&tmp, 2, 1, saveinfo->fp);
642 fflush(saveinfo->fp);
646 if (rtpinfo->info_payload_type == PT_PCMU) {
647 /* we put the pointer at the beggining of the RTP data, that is
648 * at the end of the current frame minus the length of the
649 * padding count minus length of the RTP data */
650 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
651 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
652 tmp = (gint16 )ulaw2linear((unsigned char)*data);
653 fwrite(&tmp, 2, 1, saveinfo->fp);
656 fflush(saveinfo->fp);
657 saveinfo->saved = TRUE;
662 else if (rtpinfo->info_payload_type == PT_PCMA) {
663 data = cfile.pd + (pinfo->fd->pkt_len - rtpinfo->info_payload_len);
664 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
665 tmp = (gint16 )alaw2linear((unsigned char)*data);
666 fwrite(&tmp, 2, 1, saveinfo->fp);
669 fflush(saveinfo->fp);
670 saveinfo->saved = TRUE;
673 /* comfort noise? - do nothing */
674 else if (rtpinfo->info_payload_type == PT_CN
675 || rtpinfo->info_payload_type == PT_CN_OLD) {
677 /* unsupported codec or XXX other error */
679 saveinfo->saved = FALSE;
680 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
688 /****************************************************************************/
691 /****************************************************************************/
692 /* XXX just copied from gtk/rpc_stat.c */
693 void protect_thread_critical_region(void);
694 void unprotect_thread_critical_region(void);
697 /****************************************************************************/
698 /* close the dialog window and remove the tap listener */
699 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
701 /* close and remove temporary files */
702 if (user_data->forward.saveinfo.fp != NULL)
703 fclose(user_data->forward.saveinfo.fp);
704 if (user_data->reversed.saveinfo.fp != NULL)
705 fclose(user_data->reversed.saveinfo.fp);
706 remove(user_data->f_tempname);
707 remove(user_data->r_tempname);
709 /* destroy save_voice_as window if open */
710 if (user_data->dlg.save_voice_as_w != NULL)
711 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
713 #ifdef USE_CONVERSATION_GRAPH
714 /* destroy graph window if open */
715 if (user_data->dlg.graph_window != NULL)
716 gtk_widget_destroy(user_data->dlg.graph_window);
719 g_free(user_data->dlg.col_arrows_fwd);
720 g_free(user_data->dlg.col_arrows_rev);
725 /****************************************************************************/
726 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
727 GtkNotebookPage *page _U_,
729 user_data_t *user_data _U_)
731 user_data->dlg.selected_clist =
732 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
733 user_data->dlg.selected_row = 0;
736 /****************************************************************************/
737 static void on_clist_select_row(GtkCList *clist _U_,
741 user_data_t *user_data _U_)
743 user_data->dlg.selected_clist = clist;
744 user_data->dlg.selected_row = row;
748 #ifdef USE_CONVERSATION_GRAPH
749 /****************************************************************************/
750 /* when the graph window gets destroyed */
751 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
753 /* note that graph window has been destroyed */
754 user_data->dlg.graph_window = NULL;
757 /****************************************************************************/
758 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
761 GtkCList *clist = NULL;
762 if (vp.fnumber != 0) {
763 clist = GTK_CLIST(user_data->dlg.clist_fwd);
764 row = gtk_clist_find_row_from_data(clist,
765 GUINT_TO_POINTER(vp.fnumber));
767 clist = GTK_CLIST(user_data->dlg.clist_rev);
768 row = gtk_clist_find_row_from_data(clist,
769 GUINT_TO_POINTER(vp.fnumber));
772 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
773 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
774 gtk_clist_select_row(clist, row, 0);
775 gtk_clist_moveto(clist, row, 0, 0.5, 0);
781 /****************************************************************************/
782 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
788 if (user_data->dlg.graph_window != NULL) {
789 /* There's already a graph window; reactivate it. */
790 reactivate_window(user_data->dlg.graph_window);
794 list = g_list_append(list, &(user_data->series_fwd));
795 list = g_list_append(list, &(user_data->series_rev));
797 user_data->series_fwd.color.pixel = 0;
798 user_data->series_fwd.color.red = 0x80ff;
799 user_data->series_fwd.color.green = 0xe0ff;
800 user_data->series_fwd.color.blue = 0xffff;
801 user_data->series_fwd.yvalue = 0.5;
803 user_data->series_rev.color.pixel = 0;
804 user_data->series_rev.color.red = 0x60ff;
805 user_data->series_rev.color.green = 0xc0ff;
806 user_data->series_rev.color.blue = 0xffff;
807 user_data->series_rev.yvalue = -0.5;
809 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
810 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
811 user_data->port_src_fwd,
812 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
813 user_data->port_dst_fwd,
814 user_data->ssrc_fwd);
816 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
817 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
818 user_data->port_src_rev,
819 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
820 user_data->port_dst_rev,
821 user_data->ssrc_rev);
823 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
824 &graph_selection_callback, user_data);
825 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
826 on_destroy_graph, user_data);
828 #endif /*USE_CONVERSATION_GRAPH*/
831 /****************************************************************************/
832 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
836 if (user_data->dlg.selected_clist!=NULL) {
837 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
838 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
839 goto_frame(&cfile, fnumber);
844 static void draw_stat(user_data_t *user_data);
846 /****************************************************************************/
847 /* re-dissects all packets */
848 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
850 gchar filter_text[256];
852 GString *error_string;
854 /* clear the dialog box clists */
855 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
856 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
858 /* try to compile the filter. */
859 strcpy(filter_text,"rtp && ip");
860 if (!dfilter_compile(filter_text, &sfcode)) {
861 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
865 sprintf(filter_text,"rtp && (( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ) || ( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ))",
866 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
867 user_data->port_src_fwd,
868 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
869 user_data->port_dst_fwd,
870 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
871 user_data->port_src_rev,
872 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
873 user_data->port_dst_rev
876 /* register tap listener */
877 error_string = register_tap_listener("rtp", user_data, filter_text,
878 rtp_reset, rtp_packet, rtp_draw);
879 if (error_string != NULL) {
880 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
881 g_string_free(error_string, TRUE);
885 /* retap all packets */
886 retap_packets(&cfile);
888 /* remove tap listener again */
889 protect_thread_critical_region();
890 remove_tap_listener(user_data);
891 unprotect_thread_critical_region();
893 /* draw statistics info */
894 draw_stat(user_data);
896 gtk_clist_sort(user_data->dlg.clist_fwd);
897 gtk_clist_sort(user_data->dlg.clist_rev);
900 /****************************************************************************/
901 /* on_destroy is automatically called after that */
902 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
904 gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
905 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
908 /****************************************************************************/
909 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
914 if (user_data->dlg.selected_clist==NULL)
917 clist = user_data->dlg.selected_clist;
918 row = user_data->dlg.selected_row + 1;
920 while (gtk_clist_get_text(clist,row,5,&text)) {
921 if (strcmp(text, OK_TEXT) != 0) {
922 gtk_clist_select_row(clist, row, 0);
923 gtk_clist_moveto(clist, row, 0, 0.5, 0);
931 while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
932 if (strcmp(text, OK_TEXT) != 0) {
933 gtk_clist_select_row(clist, row, 0);
934 gtk_clist_moveto(clist, row, 0, 0.5, 0);
941 /****************************************************************************/
942 /* when we want to save the information */
943 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
946 GtkWidget *rev, *forw, *both;
947 user_data_t *user_data;
953 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
955 /* Perhaps the user specified a directory instead of a file.
956 Check whether they did. */
957 if (test_for_directory(g_dest) == EISDIR) {
958 /* It's a directory - set the file selection box to display it. */
959 set_last_open_dir(g_dest);
961 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
965 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
966 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
967 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
968 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
970 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
971 fp = fopen(g_dest, "w");
973 simple_dialog(ESD_TYPE_CRIT, NULL,
974 file_open_error_message(errno, TRUE), g_dest);
978 if (GTK_TOGGLE_BUTTON(both)->active) {
979 fprintf(fp, "Forward\n");
981 simple_dialog(ESD_TYPE_CRIT, NULL,
982 file_write_error_message(errno), g_dest);
988 for(j = 0; j < 8; j++) {
990 fprintf(fp,"%s",titles[j]);
992 fprintf(fp,",%s",titles[j]);
997 simple_dialog(ESD_TYPE_CRIT, NULL,
998 file_write_error_message(errno), g_dest);
1002 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
1003 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1004 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1006 fprintf(fp,"%s",columnText);
1008 fprintf(fp,",%s",columnText);
1013 simple_dialog(ESD_TYPE_CRIT, NULL,
1014 file_write_error_message(errno), g_dest);
1020 if (fclose(fp) == EOF) {
1021 simple_dialog(ESD_TYPE_CRIT, NULL,
1022 file_write_error_message(errno), g_dest);
1027 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1029 if (GTK_TOGGLE_BUTTON(both)->active) {
1030 fp = fopen(g_dest, "a");
1032 simple_dialog(ESD_TYPE_CRIT, NULL,
1033 file_open_error_message(errno, TRUE),
1037 fprintf(fp, "\nReverse\n");
1039 simple_dialog(ESD_TYPE_CRIT, NULL,
1040 file_write_error_message(errno), g_dest);
1045 fp = fopen(g_dest, "w");
1047 simple_dialog(ESD_TYPE_CRIT, NULL,
1048 file_open_error_message(errno, TRUE),
1053 for(j = 0; j < 8; j++) {
1055 fprintf(fp,"%s",titles[j]);
1057 fprintf(fp,",%s",titles[j]);
1062 simple_dialog(ESD_TYPE_CRIT, NULL,
1063 file_write_error_message(errno), g_dest);
1067 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1068 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1069 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1071 fprintf(fp,"%s",columnText);
1073 fprintf(fp,",%s",columnText);
1078 simple_dialog(ESD_TYPE_CRIT, NULL,
1079 file_write_error_message(errno), g_dest);
1084 if (fclose(fp) == EOF) {
1085 simple_dialog(ESD_TYPE_CRIT, NULL,
1086 file_write_error_message(errno), g_dest);
1091 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1094 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1096 user_data->dlg.save_csv_as_w = NULL;
1099 /* when the user wants to save the csv information in a file */
1100 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
1104 GtkWidget *label_format;
1105 GtkWidget *channels_label;
1106 GSList *channels_group = NULL;
1107 GtkWidget *forward_rb;
1108 GtkWidget *reversed_rb;
1112 if (user_data->dlg.save_csv_as_w != NULL) {
1113 /* There's already a Save CSV info dialog box; reactivate it. */
1114 reactivate_window(user_data->dlg.save_csv_as_w);
1118 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
1119 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
1120 save_csv_as_destroy_cb, user_data);
1122 /* Container for each row of widgets */
1123 vertb = gtk_vbox_new(FALSE, 0);
1124 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1125 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
1126 vertb, FALSE, FALSE, 0);
1127 gtk_widget_show (vertb);
1129 table1 = gtk_table_new (2, 4, FALSE);
1130 gtk_widget_show (table1);
1131 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1132 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1133 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1135 label_format = gtk_label_new ("Format: Comma Separated Values");
1136 gtk_widget_show (label_format);
1137 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1138 (GtkAttachOptions) (GTK_FILL),
1139 (GtkAttachOptions) (0), 0, 0);
1142 channels_label = gtk_label_new ("Channels:");
1143 gtk_widget_show (channels_label);
1144 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1145 (GtkAttachOptions) (GTK_FILL),
1146 (GtkAttachOptions) (0), 0, 0);
1147 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1149 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1150 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1151 gtk_widget_show (forward_rb);
1152 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1153 (GtkAttachOptions) (GTK_FILL),
1154 (GtkAttachOptions) (0), 0, 0);
1156 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1157 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1158 gtk_widget_show (reversed_rb);
1159 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1160 (GtkAttachOptions) (GTK_FILL),
1161 (GtkAttachOptions) (0), 0, 0);
1163 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1164 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1165 gtk_widget_show (both_rb);
1166 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1167 (GtkAttachOptions) (GTK_FILL),
1168 (GtkAttachOptions) (0), 0, 0);
1170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1172 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1173 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1174 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1175 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1176 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1178 /* Connect the cancel_button to destroy the widget */
1179 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1180 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1181 user_data->dlg.save_csv_as_w);
1183 /* Catch the "key_press_event" signal in the window, so that we can catch
1184 the ESC key being pressed and act as if the "Cancel" button had
1186 dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1188 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
1189 user_data->dlg.save_csv_as_w);
1191 gtk_widget_show(user_data->dlg.save_csv_as_w);
1195 /****************************************************************************/
1196 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1198 /* Note that we no longer have a Save voice info dialog box. */
1199 user_data->dlg.save_voice_as_w = NULL;
1202 /****************************************************************************/
1203 /* here we save it into a file that user specified */
1204 /* XXX what about endians here? could go something wrong? */
1205 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1207 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1211 guint32 f_write_silence = 0;
1212 guint32 r_write_silence = 0;
1214 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1215 gboolean stop_flag = FALSE;
1217 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1220 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1226 /* open file for saving */
1227 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1234 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
1236 /* First we write the .au header. XXX Hope this is endian independant */
1237 /* the magic word 0x2e736e64 == .snd */
1238 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1239 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1240 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1241 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1242 /* header offset == 24 bytes */
1243 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1244 write(to_fd, pd, 1);
1245 write(to_fd, pd, 1);
1246 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1247 /* total length, it is permited to set this to 0xffffffff */
1248 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1249 write(to_fd, pd, 1);
1250 write(to_fd, pd, 1);
1251 write(to_fd, pd, 1);
1252 /* encoding format == 8 bit ulaw */
1253 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1254 write(to_fd, pd, 1);
1255 write(to_fd, pd, 1);
1256 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1257 /* sample rate == 8000 Hz */
1258 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1259 write(to_fd, pd, 1);
1260 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1261 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1263 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1264 write(to_fd, pd, 1);
1265 write(to_fd, pd, 1);
1266 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1269 /* only forward direction */
1271 progbar_count = user_data->forward.saveinfo.count;
1272 progbar_quantum = user_data->forward.saveinfo.count/100;
1273 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1276 if((count > progbar_nextstep) && (count <= progbar_count)) {
1277 update_progress_dlg(progbar,
1278 (gfloat) count/progbar_count, "Saving");
1279 progbar_nextstep = progbar_nextstep + progbar_quantum;
1282 *pd = (unsigned char)linear2ulaw(f_pd);
1283 fwritten = write(to_fd, pd, 1);
1284 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1288 destroy_progress_dlg(progbar);
1294 /* only reversed direction */
1296 progbar_count = user_data->reversed.saveinfo.count;
1297 progbar_quantum = user_data->reversed.saveinfo.count/100;
1298 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1301 if((count > progbar_nextstep) && (count <= progbar_count)) {
1302 update_progress_dlg(progbar,
1303 (gfloat) count/progbar_count, "Saving");
1304 progbar_nextstep = progbar_nextstep + progbar_quantum;
1307 *pd = (unsigned char)linear2ulaw(r_pd);
1308 rwritten = write(to_fd, pd, 1);
1309 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1313 destroy_progress_dlg(progbar);
1319 /* both directions */
1321 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
1322 (progbar_count = user_data->forward.saveinfo.count) :
1323 (progbar_count = user_data->reversed.saveinfo.count);
1324 progbar_quantum = progbar_count/100;
1325 /* since conversation in one way can start later than in the other one,
1326 * we have to write some silence information for one channel */
1327 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1328 f_write_silence = (guint32)
1329 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
1331 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1332 r_write_silence = (guint32)
1333 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
1338 if((count > progbar_nextstep) && (count <= progbar_count)) {
1339 update_progress_dlg(progbar,
1340 (gfloat) count/progbar_count, "Saving");
1341 progbar_nextstep = progbar_nextstep + progbar_quantum;
1344 if(f_write_silence > 0) {
1345 rread = read(rev_fd, &r_pd, 2);
1350 else if(r_write_silence > 0) {
1351 fread = read(forw_fd, &f_pd, 2);
1357 fread = read(forw_fd, &f_pd, 2);
1358 rread = read(rev_fd, &r_pd, 2);
1360 if ((rread == 0) && (fread == 0))
1362 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1363 rwritten = write(to_fd, pd, 1);
1364 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1368 destroy_progress_dlg(progbar);
1374 destroy_progress_dlg(progbar);
1382 /****************************************************************************/
1383 /* the user wants to save in a file */
1384 /* XXX support for different formats is currently commented out */
1385 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1388 /*GtkWidget *wav, *au, *sw;*/
1389 GtkWidget *rev, *forw, *both;
1390 user_data_t *user_data;
1391 gint channels /*, format*/;
1393 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1395 /* Perhaps the user specified a directory instead of a file.
1396 Check whether they did. */
1397 if (test_for_directory(g_dest) == EISDIR) {
1398 /* It's a directory - set the file selection box to display it. */
1399 set_last_open_dir(g_dest);
1401 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1405 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1406 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1407 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1408 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1409 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1410 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1411 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1413 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1414 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1415 * disable the ok button or disable the buttons for direction if only one is not ok. The
1416 * problem is if we open the save voice dialog and then click the refresh button and maybe
1417 * the state changes, so we can't save anymore. In this case we should be able to update
1418 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1421 /* we can not save in both dirctions */
1422 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1423 /* there are many combinations here, we just exit when first matches */
1424 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
1425 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1426 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1427 "Can't save in a file: Unsupported codec!");
1428 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
1429 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1430 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1431 "Can't save in a file: Wrong length of captured packets!");
1432 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
1433 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1434 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1435 "Can't save in a file: RTP data with padding!");
1437 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1438 "Can't save in a file: File I/O problem!");
1441 /* we can not save forward direction */
1442 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1443 (GTK_TOGGLE_BUTTON (both)->active))) {
1444 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1445 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1446 "Can't save forward direction in a file: Unsupported codec!");
1447 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1448 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1449 "Can't save forward direction in a file: Wrong length of captured packets!");
1450 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1451 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1452 "Can't save forward direction in a file: RTP data with padding!");
1454 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1455 "Can't save forward direction in a file: File I/O problem!");
1458 /* we can not save reversed direction */
1459 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1460 (GTK_TOGGLE_BUTTON (both)->active))) {
1461 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1462 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1463 "Can't save reversed direction in a file: Unsupported codec!");
1464 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1465 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1466 "Can't save reversed direction in a file: Wrong length of captured packets!");
1467 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1468 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1469 "Can't save reversed direction in a file: RTP data with padding!");
1470 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1471 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1472 "Can't save reversed direction in a file: No RTP data!");
1474 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1475 "Can't save reversed direction in a file: File I/O problem!");
1479 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1481 else if (GTK_TOGGLE_BUTTON (au)->active)
1483 else if (GTK_TOGGLE_BUTTON (sw)->active)
1486 if (GTK_TOGGLE_BUTTON (rev)->active)
1488 else if (GTK_TOGGLE_BUTTON (both)->active)
1493 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1494 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
1495 "An error occured while saving voice in a file!");
1499 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1502 /****************************************************************************/
1503 /* when the user wants to save the voice information in a file */
1504 /* XXX support for different formats is currently commented out */
1505 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1509 GtkWidget *label_format;
1510 GtkWidget *channels_label;
1511 /*GSList *format_group = NULL;*/
1512 GSList *channels_group = NULL;
1513 GtkWidget *forward_rb;
1514 GtkWidget *reversed_rb;
1516 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1519 /* if we can't save in a file: wrong codec, cut packets or other errors */
1520 /* shold the error arise here or later when you click ok button ?
1521 * if we do it here, then we must disable the refresh button, so we don't do it here */
1523 if (user_data->dlg.save_voice_as_w != NULL) {
1524 /* There's already a Save voice info dialog box; reactivate it. */
1525 reactivate_window(user_data->dlg.save_voice_as_w);
1529 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1530 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
1531 save_voice_as_destroy_cb, user_data);
1533 /* Container for each row of widgets */
1534 vertb = gtk_vbox_new(FALSE, 0);
1535 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1536 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1537 vertb, FALSE, FALSE, 0);
1538 gtk_widget_show (vertb);
1540 table1 = gtk_table_new (2, 4, FALSE);
1541 gtk_widget_show (table1);
1542 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1543 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1544 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1546 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1547 gtk_widget_show (label_format);
1548 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1549 (GtkAttachOptions) (GTK_FILL),
1550 (GtkAttachOptions) (0), 0, 0);
1552 /* we support .au - ulaw*/
1553 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1554 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1555 gtk_widget_show (wav_rb);
1556 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1557 (GtkAttachOptions) (GTK_FILL),
1558 (GtkAttachOptions) (0), 0, 0);
1560 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1561 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1562 gtk_widget_show (sw_rb);
1563 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1564 (GtkAttachOptions) (GTK_FILL),
1565 (GtkAttachOptions) (0), 0, 0);
1566 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1567 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1568 gtk_widget_show (au_rb);
1569 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1570 (GtkAttachOptions) (GTK_FILL),
1571 (GtkAttachOptions) (0), 0, 0);
1574 channels_label = gtk_label_new ("Channels:");
1575 gtk_widget_show (channels_label);
1576 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1577 (GtkAttachOptions) (GTK_FILL),
1578 (GtkAttachOptions) (0), 0, 0);
1579 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1581 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1582 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1583 gtk_widget_show (forward_rb);
1584 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1585 (GtkAttachOptions) (GTK_FILL),
1586 (GtkAttachOptions) (0), 0, 0);
1588 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1589 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1590 gtk_widget_show (reversed_rb);
1591 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1592 (GtkAttachOptions) (GTK_FILL),
1593 (GtkAttachOptions) (0), 0, 0);
1595 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1596 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1597 gtk_widget_show (both_rb);
1598 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1599 (GtkAttachOptions) (GTK_FILL),
1600 (GtkAttachOptions) (0), 0, 0);
1602 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1604 /* if one direction is nok we don't allow saving
1605 XXX this is not ok since the user can click the refresh button and cause changes
1606 but we can not update this window. So we move all the decision on the time the ok
1608 if (user_data->forward.saved == FALSE) {
1609 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1610 gtk_widget_set_sensitive(forward_rb, FALSE);
1611 gtk_widget_set_sensitive(both_rb, FALSE);
1613 else if (user_data->reversed.saved == FALSE) {
1614 gtk_widget_set_sensitive(reversed_rb, FALSE);
1615 gtk_widget_set_sensitive(both_rb, FALSE);
1619 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1620 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1621 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1622 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1623 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1624 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1625 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1626 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1628 /* Connect the cancel_button to destroy the widget */
1629 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1630 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1631 user_data->dlg.save_voice_as_w);
1633 /* Catch the "key_press_event" signal in the window, so that we can catch
1634 the ESC key being pressed and act as if the "Cancel" button had
1636 dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1638 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
1639 user_data->dlg.save_voice_as_w);
1641 gtk_widget_show(user_data->dlg.save_voice_as_w);
1645 /****************************************************************************/
1646 /* when we are finished with redisection, we add the label for the statistic */
1647 static void draw_stat(user_data_t *user_data)
1649 gchar label_max[200];
1650 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1651 - user_data->forward.statinfo.start_seq_nr + 1;
1652 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1653 - user_data->reversed.statinfo.start_seq_nr + 1;
1654 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1655 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1657 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1658 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1659 " Sequence errors = %u",
1660 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr,
1661 user_data->forward.statinfo.total_nr,
1662 f_expected, f_lost, user_data->forward.statinfo.sequence);
1664 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1666 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1667 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1668 " Sequence errors = %u",
1669 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr,
1670 user_data->reversed.statinfo.total_nr,
1671 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1673 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1681 /****************************************************************************/
1682 /* append a line to clist */
1683 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1684 double delay, double jitter, gchar *status, gboolean marker,
1685 gchar *timeStr, guint32 pkt_len, GdkColor *color)
1691 data[0]=&field[0][0];
1692 data[1]=&field[1][0];
1693 data[2]=&field[2][0];
1694 data[3]=&field[3][0];
1695 data[4]=&field[4][0];
1696 data[5]=&field[5][0];
1697 data[6]=&field[6][0];
1698 data[7]=&field[7][0];
1700 g_snprintf(field[0], 20, "%u", number);
1701 g_snprintf(field[1], 20, "%u", seq_num);
1702 g_snprintf(field[2], 20, "%f", delay);
1703 g_snprintf(field[3], 20, "%f", jitter);
1704 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1705 g_snprintf(field[5], 40, "%s", status);
1706 g_snprintf(field[6], 32, "%s", timeStr);
1707 g_snprintf(field[7], 20, "%u", pkt_len);
1709 added_row = gtk_clist_append(GTK_CLIST(clist), data);
1710 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1711 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1715 /****************************************************************************/
1716 /* callback for sorting columns of clist */
1717 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
1724 const GtkCListRow *row1 = (GtkCListRow *) ptr1;
1725 const GtkCListRow *row2 = (GtkCListRow *) ptr2;
1727 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
1728 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
1730 switch(clist->sort_column){
1731 /* columns representing strings */
1735 return strcmp (text1, text2);
1736 /* columns representing ints */
1743 /* columns representing floats */
1748 if (f1<f2) return -1;
1749 else if (f1>f2) return 1;
1752 g_assert_not_reached();
1757 /****************************************************************************/
1759 click_column_cb(GtkCList *clist, gint column, gpointer data)
1761 column_arrows *col_arrows = (column_arrows *) data;
1764 gtk_clist_freeze(clist);
1766 for (i = 0; i < NUM_COLS; i++) {
1767 gtk_widget_hide(col_arrows[i].ascend_pm);
1768 gtk_widget_hide(col_arrows[i].descend_pm);
1771 if (column == clist->sort_column) {
1772 if (clist->sort_type == GTK_SORT_ASCENDING) {
1773 clist->sort_type = GTK_SORT_DESCENDING;
1774 gtk_widget_show(col_arrows[column].descend_pm);
1776 clist->sort_type = GTK_SORT_ASCENDING;
1777 gtk_widget_show(col_arrows[column].ascend_pm);
1780 clist->sort_type = GTK_SORT_ASCENDING;
1781 gtk_widget_show(col_arrows[column].ascend_pm);
1782 gtk_clist_set_sort_column(clist, column);
1784 gtk_clist_thaw(clist);
1786 gtk_clist_sort(clist);
1790 /****************************************************************************/
1791 /* Add the packet list */
1793 GtkWidget* create_clist(user_data_t* user_data)
1795 GtkWidget* clist_fwd;
1797 /* clist for the information */
1798 clist_fwd = gtk_clist_new(8);
1799 gtk_widget_show(clist_fwd);
1800 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
1802 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
1803 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
1804 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
1805 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
1807 /* hide date and length column */
1808 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1809 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1811 /* column widths and justification */
1812 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
1813 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
1814 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
1815 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
1816 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
1817 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
1818 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
1819 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1820 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1821 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1822 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1828 /****************************************************************************/
1829 /* Add the sort by column feature for a packet clist */
1831 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
1832 user_data_t* user_data _U_)
1834 column_arrows *col_arrows;
1835 GdkBitmap *ascend_bm, *descend_bm;
1836 GdkPixmap *ascend_pm, *descend_pm;
1837 GtkStyle *win_style;
1838 GtkWidget *column_lb;
1841 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
1842 win_style = gtk_widget_get_style(window);
1843 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1845 &win_style->bg[GTK_STATE_NORMAL],
1846 (gchar **)clist_ascend_xpm);
1847 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1849 &win_style->bg[GTK_STATE_NORMAL],
1850 (gchar **)clist_descend_xpm);
1852 for (i=0; i<NUM_COLS; i++) {
1853 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1854 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1855 column_lb = gtk_label_new(titles[i]);
1856 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1857 gtk_widget_show(column_lb);
1859 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1860 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1861 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1862 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1863 /* make packet-nr be the default sort order */
1865 gtk_widget_show(col_arrows[i].ascend_pm);
1867 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
1868 gtk_widget_show(col_arrows[i].table);
1871 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
1876 /****************************************************************************/
1877 /* Create the dialog box with all widgets */
1878 void create_rtp_dialog(user_data_t* user_data)
1880 GtkWidget *window = NULL;
1881 GtkWidget *clist_fwd;
1882 GtkWidget *clist_rev;
1883 GtkWidget *label_stats_fwd;
1884 GtkWidget *label_stats_rev;
1885 GtkWidget *notebook;
1887 GtkWidget *main_vb, *page, *page_r;
1889 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1890 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1891 #ifdef USE_CONVERSATION_GRAPH
1892 GtkWidget *graph_bt;
1895 gchar label_forward[150];
1896 gchar label_reverse[150];
1898 gchar str_ip_src[16];
1899 gchar str_ip_dst[16];
1900 column_arrows *col_arrows_fwd;
1901 column_arrows *col_arrows_rev;
1904 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1905 gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1906 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1907 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
1909 /* Container for each row of widgets */
1910 main_vb = gtk_vbox_new(FALSE, 2);
1911 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
1912 gtk_container_add(GTK_CONTAINER(window), main_vb);
1913 gtk_widget_show(main_vb);
1916 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1917 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1919 g_snprintf(label_forward, 149,
1920 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1921 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1923 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1924 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1926 g_snprintf(label_reverse, 149,
1927 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1928 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1930 /* Start a notebook for flipping between sets of changes */
1931 notebook = gtk_notebook_new();
1932 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1933 OBJECT_SET_DATA(window, "notebook", notebook);
1934 SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
1937 /* page for forward connection */
1938 page = gtk_vbox_new(FALSE, 8);
1939 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1941 /* direction label */
1942 label = gtk_label_new(label_forward);
1943 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
1945 /* place for some statistics */
1946 label_stats_fwd = gtk_label_new("\n");
1947 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
1949 /* scrolled window */
1950 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1951 WIDGET_SET_SIZE(scrolled_window, 560, 200);
1952 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1953 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1956 clist_fwd = create_clist(user_data);
1957 gtk_widget_show(clist_fwd);
1958 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1959 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1960 gtk_widget_show(scrolled_window);
1963 label = gtk_label_new(" Forward Direction ");
1964 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1966 /* same page for reversed connection */
1967 page_r = gtk_vbox_new(FALSE, 8);
1968 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
1969 label = gtk_label_new(label_reverse);
1970 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
1971 label_stats_rev = gtk_label_new("\n");
1972 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
1974 scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
1975 WIDGET_SET_SIZE(scrolled_window_r, 560, 200);
1976 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r),
1977 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1979 clist_rev = create_clist(user_data);
1980 gtk_widget_show(clist_rev);
1981 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
1982 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
1983 gtk_widget_show(scrolled_window_r);
1985 label = gtk_label_new(" Reversed Direction ");
1986 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
1988 /* page for help&about or future
1989 page_help = gtk_hbox_new(FALSE, 5);
1990 label = gtk_label_new(" Future ");
1991 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
1992 frame = gtk_frame_new("");
1993 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
1994 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
1995 gtk_container_add(GTK_CONTAINER(frame), text);
1996 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
1997 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
2000 /* show all notebooks */
2001 gtk_widget_show_all(notebook);
2004 box4 = gtk_hbutton_box_new();
2005 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
2006 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
2007 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
2008 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
2009 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
2010 gtk_widget_show(box4);
2012 voice_bt = gtk_button_new_with_label("Save payload...");
2013 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
2014 gtk_widget_show(voice_bt);
2015 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
2017 csv_bt = gtk_button_new_with_label("Save as CSV...");
2018 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
2019 gtk_widget_show(csv_bt);
2020 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
2022 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
2023 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
2024 gtk_widget_show(refresh_bt);
2025 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
2027 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
2028 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
2029 gtk_widget_show(goto_bt);
2030 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
2032 #ifdef USE_CONVERSATION_GRAPH
2033 graph_bt = gtk_button_new_with_label("Graph");
2034 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
2035 gtk_widget_show(graph_bt);
2036 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
2039 next_bt = gtk_button_new_with_label("Next non-Ok");
2040 gtk_container_add(GTK_CONTAINER(box4), next_bt);
2041 gtk_widget_show(next_bt);
2042 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
2044 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
2045 gtk_container_add(GTK_CONTAINER(box4), close_bt);
2046 gtk_widget_show(close_bt);
2047 SIGNAL_CONNECT(close_bt, "clicked", on_close_bt_clicked, user_data);
2049 gtk_widget_show(window);
2051 /* sort by column feature */
2052 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
2053 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
2055 /* some widget references need to be saved for outside use */
2056 user_data->dlg.window = window;
2057 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
2058 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
2059 user_data->dlg.label_stats_fwd = label_stats_fwd;
2060 user_data->dlg.label_stats_rev = label_stats_rev;
2061 user_data->dlg.notebook = notebook;
2062 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
2063 user_data->dlg.selected_row = 0;
2064 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
2065 user_data->dlg.col_arrows_rev = col_arrows_rev;
2069 /****************************************************************************/
2070 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
2071 gchar* proto_field, guint32* p_result)
2074 proto_node *proto_sibling_node;
2075 header_field_info *hfssrc;
2078 finfo = PITEM_FINFO(ptree_node);
2080 if (hfinformation==(finfo->hfinfo)) {
2081 hfssrc = proto_registrar_get_byname(proto_field);
2084 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
2085 ptree_node=ptree_node->next) {
2086 finfo=PITEM_FINFO(ptree_node);
2087 if (hfssrc==finfo->hfinfo) {
2088 if (hfinformation->type==FT_IPv4) {
2089 ipv4 = fvalue_get(&finfo->value);
2090 *p_result = ipv4_get_net_order_addr(ipv4);
2093 *p_result = fvalue_get_integer(&finfo->value);
2100 proto_sibling_node = ptree_node->next;
2102 if (proto_sibling_node) {
2103 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
2109 /****************************************************************************/
2110 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
2115 proto_node *ptree_node;
2116 header_field_info *hfinformation;
2118 hfinformation = proto_registrar_get_byname(proto_name);
2119 if (hfinformation == NULL)
2122 ptree_node = ((proto_node *)protocol_tree)->first_child;
2126 return process_node(ptree_node, hfinformation, proto_field, p_result);
2130 /* XXX just copied from gtk/rpc_stat.c */
2131 void protect_thread_critical_region(void);
2132 void unprotect_thread_critical_region(void);
2134 /****************************************************************************/
2135 /* XXX only handles RTP over IPv4, should add IPv6 support */
2138 guint16 port_src_fwd,
2140 guint16 port_dst_fwd,
2143 guint16 port_src_rev,
2145 guint16 port_dst_rev,
2149 user_data_t *user_data;
2152 user_data = g_malloc(sizeof(user_data_t));
2154 user_data->ip_src_fwd = ip_src_fwd;
2155 user_data->port_src_fwd = port_src_fwd;
2156 user_data->ip_dst_fwd = ip_dst_fwd;
2157 user_data->port_dst_fwd = port_dst_fwd;
2158 user_data->ssrc_fwd = ssrc_fwd;
2159 user_data->ip_src_rev = ip_src_rev;
2160 user_data->port_src_rev = port_src_rev;
2161 user_data->ip_dst_rev = ip_dst_rev;
2162 user_data->port_dst_rev = port_dst_rev;
2163 user_data->ssrc_rev = ssrc_rev;
2165 /* file names for storing sound data */
2166 strncpy(user_data->f_tempname, "f_tempnameXXXXXX", TMPNAMSIZE);
2167 strncpy(user_data->r_tempname, "r_tempnameXXXXXX", TMPNAMSIZE);
2168 mkstemp(user_data->f_tempname);
2169 mkstemp(user_data->r_tempname);
2170 user_data->forward.saveinfo.fp = NULL;
2171 user_data->reversed.saveinfo.fp = NULL;
2172 user_data->dlg.save_voice_as_w = NULL;
2173 user_data->dlg.save_csv_as_w = NULL;
2174 #ifdef USE_CONVERSATION_GRAPH
2175 user_data->dlg.graph_window = NULL;
2176 user_data->series_fwd.value_pairs = NULL;
2177 user_data->series_rev.value_pairs = NULL;
2180 /* create the dialog box */
2181 create_rtp_dialog(user_data);
2183 /* proceed as if the Refresh button would have been pressed */
2184 on_refresh_bt_clicked(NULL, user_data);
2187 /****************************************************************************/
2188 /* entry point from main menu */
2189 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
2192 guint16 port_src_fwd;
2194 guint16 port_dst_fwd;
2195 guint32 ssrc_fwd = 0;
2197 guint16 port_src_rev;
2199 guint16 port_dst_rev;
2200 guint32 ssrc_rev = 0;
2202 gchar filter_text[256];
2205 epan_dissect_t *edt;
2207 gboolean frame_matched;
2209 GList *strinfo_list;
2210 GList *filtered_list = NULL;
2211 rtp_stream_info_t *strinfo;
2214 /* Try to compile the filter. */
2215 strcpy(filter_text,"rtp && ip");
2216 if (!dfilter_compile(filter_text, &sfcode)) {
2217 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
2220 /* we load the current file into cf variable */
2222 fdata = cf->current_frame;
2224 /* we are on the selected frame now */
2226 return; /* if we exit here it's an error */
2228 /* dissect the current frame */
2229 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) {
2230 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
2231 file_read_error_message(err), cf->filename);
2234 edt = epan_dissect_new(TRUE, FALSE);
2235 epan_dissect_prime_dfilter(edt, sfcode);
2236 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
2237 frame_matched = dfilter_apply_edt(sfcode, edt);
2239 /* if it is not an rtp frame, show the rtpstream dialog */
2240 frame_matched = dfilter_apply_edt(sfcode, edt);
2241 if (frame_matched != 1) {
2242 epan_dissect_free(edt);
2243 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!");
2247 /* ok, it is a RTP frame, so let's get the ip and port values */
2248 g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
2249 g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
2250 port_src_fwd = edt->pi.srcport;
2251 port_dst_fwd = edt->pi.destport;
2253 /* assume the inverse ip/port combination for the reverse direction */
2254 g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
2255 g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
2256 port_src_rev = edt->pi.destport;
2257 port_dst_rev = edt->pi.srcport;
2259 /* now we need the SSRC value of the current frame */
2260 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
2261 simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!");
2265 /* search for reversed direction in the global rtp streams list */
2267 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
2268 while (strinfo_list)
2270 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
2271 if (strinfo->src_addr==ip_src_fwd
2272 && strinfo->src_port==port_src_fwd
2273 && strinfo->dest_addr==ip_dst_fwd
2274 && strinfo->dest_port==port_dst_fwd)
2276 filtered_list = g_list_prepend(filtered_list, strinfo);
2279 if (strinfo->src_addr==ip_src_rev
2280 && strinfo->src_port==port_src_rev
2281 && strinfo->dest_addr==ip_dst_rev
2282 && strinfo->dest_port==port_dst_rev)
2285 filtered_list = g_list_append(filtered_list, strinfo);
2287 ssrc_rev = strinfo->ssrc;
2290 strinfo_list = g_list_next(strinfo_list);
2293 /* if more than one reverse streams found, we let the user choose the right one */
2295 rtpstream_dlg_show(filtered_list);
2314 /****************************************************************************/
2316 rtp_analysis_init(char *dummy _U_)
2318 rtp_analysis_cb(NULL, NULL);
2321 /****************************************************************************/
2323 register_tap_listener_rtp_analysis(void)
2325 register_ethereal_tap("rtp", rtp_analysis_init);
2329 register_tap_menu_rtp_analysis(void)
2331 register_tap_menu_item("_Statistics/RTP Streams/Analyse...",
2332 rtp_analysis_cb, NULL, NULL, NULL);