2 * RTP analysis addition for ethereal
4 * $Id: rtp_analysis.c,v 1.33 2004/02/11 01:23:25 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"
48 #include "epan/epan_dissect.h"
49 #include "epan/filesystem.h"
52 #include "packet-rtp.h"
61 #include "dlg_utils.h"
63 #include "alert_box.h"
64 #include "simple_dialog.h"
67 #include "progress_dlg.h"
68 #include "compat_macros.h"
70 #include "image/clist_ascend.xpm"
71 #include "image/clist_descend.xpm"
82 #include <io.h> /* open/close on win32 */
89 /****************************************************************************/
91 typedef struct column_arrows {
94 GtkWidget *descend_pm;
97 typedef struct _dialog_data_t {
101 GtkWidget *label_stats_fwd;
102 GtkWidget *label_stats_rev;
103 column_arrows *col_arrows_fwd;
104 column_arrows *col_arrows_rev;
106 GtkCList *selected_clist;
107 GtkWidget *save_voice_as_w;
108 GtkWidget *save_csv_as_w;
110 #ifdef USE_CONVERSATION_GRAPH
111 GtkWidget *graph_window;
115 #define OK_TEXT "[ Ok ]"
116 #define PT_UNDEFINED -1
118 /* type of error when saving voice in a file didn't succeed */
121 TAP_RTP_WRONG_LENGTH,
122 TAP_RTP_PADDING_ERROR,
124 TAP_RTP_FILE_OPEN_ERROR,
129 /****************************************************************************/
130 /* structure that holds the information about the forward and reversed direction */
131 typedef struct _tap_rtp_stat_t {
132 gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
133 /* use (flags & STAT_FLAG_FIRST) instead */
134 /* all of the following fields will be initialized after
135 rtp_packet_analyse has been called */
136 guint32 flags; /* see STAT_FLAG-defines below */
139 guint32 delta_timestamp;
146 guint16 start_seq_nr;
156 /* status flags for the flags parameter in tap_rtp_stat_t */
157 #define STAT_FLAG_FIRST 0x01
158 #define STAT_FLAG_MARKER 0x02
159 #define STAT_FLAG_WRONG_SEQ 0x04
160 #define STAT_FLAG_PT_CHANGE 0x08
161 #define STAT_FLAG_PT_CN 0x10
162 #define STAT_FLAG_FOLLOW_PT_CN 0x20
163 #define STAT_FLAG_REG_PT_CHANGE 0x40
165 typedef struct _tap_rtp_save_info_t {
168 error_type_t error_type;
170 } tap_rtp_save_info_t;
173 /* structure that holds the information about the forward and reversed direction */
174 struct _info_direction {
175 tap_rtp_stat_t statinfo;
176 tap_rtp_save_info_t saveinfo;
179 #define TMPNAMSIZE 100
181 /* structure that holds general information about the connection
182 * and structures for both directions */
183 typedef struct _user_data_t {
184 /* tap associated data*/
186 guint16 port_src_fwd;
188 guint16 port_dst_fwd;
191 guint16 port_src_rev;
193 guint16 port_dst_rev;
196 struct _info_direction forward;
197 struct _info_direction reversed;
199 char f_tempname[TMPNAMSIZE];
200 char r_tempname[TMPNAMSIZE];
202 /* dialog associated data */
205 #ifdef USE_CONVERSATION_GRAPH
206 time_series_t series_fwd;
207 time_series_t series_rev;
213 static gchar *titles[8] = {
225 typedef const guint8 * ip_addr_p;
228 /****************************************************************************/
231 /****************************************************************************/
232 /* when there is a [re]reading of packet's */
234 rtp_reset(void *user_data_arg)
236 user_data_t *user_data = user_data_arg;
238 user_data->forward.statinfo.first_packet = TRUE;
239 user_data->reversed.statinfo.first_packet = TRUE;
240 user_data->forward.statinfo.max_delay = 0;
241 user_data->reversed.statinfo.max_delay = 0;
242 user_data->forward.statinfo.delay = 0;
243 user_data->reversed.statinfo.delay = 0;
244 user_data->forward.statinfo.jitter = 0;
245 user_data->reversed.statinfo.jitter = 0;
246 user_data->forward.statinfo.timestamp = 0;
247 user_data->reversed.statinfo.timestamp = 0;
248 user_data->forward.statinfo.max_nr = 0;
249 user_data->reversed.statinfo.max_nr = 0;
250 user_data->forward.statinfo.total_nr = 0;
251 user_data->reversed.statinfo.total_nr = 0;
252 user_data->forward.statinfo.sequence = 0;
253 user_data->reversed.statinfo.sequence = 0;
254 user_data->forward.statinfo.start_seq_nr = 0;
255 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
256 user_data->forward.statinfo.stop_seq_nr = 0;
257 user_data->reversed.statinfo.stop_seq_nr = 0;
258 user_data->forward.statinfo.cycles = 0;
259 user_data->reversed.statinfo.cycles = 0;
260 user_data->forward.statinfo.under = FALSE;
261 user_data->reversed.statinfo.under = FALSE;
262 user_data->forward.statinfo.start_time = 0;
263 user_data->reversed.statinfo.start_time = 0;
264 user_data->forward.statinfo.time = 0;
265 user_data->reversed.statinfo.time = 0;
266 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
267 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
269 user_data->forward.saveinfo.count = 0;
270 user_data->reversed.saveinfo.count = 0;
271 user_data->forward.saveinfo.saved = FALSE;
272 user_data->reversed.saveinfo.saved = FALSE;
274 #ifdef USE_CONVERSATION_GRAPH
275 if (user_data->dlg.graph_window != NULL)
276 gtk_widget_destroy(user_data->dlg.graph_window);
278 g_array_free(user_data->series_fwd.value_pairs, TRUE);
279 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
281 g_array_free(user_data->series_rev.value_pairs, TRUE);
282 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
285 /* XXX check for error at fclose? */
286 if (user_data->forward.saveinfo.fp != NULL)
287 fclose(user_data->forward.saveinfo.fp);
288 if (user_data->reversed.saveinfo.fp != NULL)
289 fclose(user_data->reversed.saveinfo.fp);
290 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
291 if (user_data->forward.saveinfo.fp == NULL)
292 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
293 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
294 if (user_data->reversed.saveinfo.fp == NULL)
295 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
299 /****************************************************************************/
300 /* here we can redraw the output */
302 static void rtp_draw(void *prs _U_)
307 /* forward declarations */
308 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
309 double delay, double jitter, gchar *status, gboolean marker,
310 gchar *timeStr, guint32 pkt_len, GdkColor *color);
312 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
313 packet_info *pinfo, struct _rtp_info *rtpinfo);
314 static int rtp_packet_add_info(GtkCList *clist,
315 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
316 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
317 tap_rtp_stat_t *statinfo,
318 packet_info *pinfo, struct _rtp_info *rtpinfo);
321 /****************************************************************************/
322 /* whenever a RTP packet is seen by the tap listener */
323 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *rtpinfo_arg)
325 user_data_t *user_data = user_data_arg;
326 struct _rtp_info *rtpinfo = rtpinfo_arg;
327 #ifdef USE_CONVERSATION_GRAPH
331 /* we ignore packets that are not displayed */
332 if (pinfo->fd->flags.passed_dfilter == 0)
335 /* is it the forward direction? */
336 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
337 #ifdef USE_CONVERSATION_GRAPH
338 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
339 vp.fnumber = pinfo->fd->num;
340 g_array_append_val(user_data->series_fwd.value_pairs, vp);
342 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
343 rtp_packet_add_info(user_data->dlg.clist_fwd,
344 &(user_data->forward.statinfo), pinfo, rtpinfo);
345 rtp_packet_save_payload(&(user_data->forward.saveinfo),
346 &(user_data->forward.statinfo), pinfo, rtpinfo);
348 /* is it the reversed direction? */
349 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
350 #ifdef USE_CONVERSATION_GRAPH
351 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
352 vp.fnumber = pinfo->fd->num;
353 g_array_append_val(user_data->series_rev.value_pairs, vp);
355 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
356 rtp_packet_add_info(user_data->dlg.clist_rev,
357 &(user_data->reversed.statinfo), pinfo, rtpinfo);
358 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
359 &(user_data->reversed.statinfo), pinfo, rtpinfo);
366 /****************************************************************************/
367 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
368 packet_info *pinfo, struct _rtp_info *rtpinfo)
371 double current_jitter;
375 /* check payload type */
376 if (rtpinfo->info_payload_type == PT_CN
377 || rtpinfo->info_payload_type == PT_CN_OLD)
378 statinfo->flags |= STAT_FLAG_PT_CN;
379 if (statinfo->pt == PT_CN
380 || statinfo->pt == PT_CN_OLD)
381 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
382 if (rtpinfo->info_payload_type != statinfo->pt)
383 statinfo->flags |= STAT_FLAG_PT_CHANGE;
385 statinfo->pt = rtpinfo->info_payload_type;
387 /* store the current time and calculate the current jitter */
388 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
389 current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
390 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
391 statinfo->delay = current_time-(statinfo->time);
392 statinfo->jitter = current_jitter;
394 /* is this the first packet we got in this direction? */
395 if (statinfo->first_packet) {
396 statinfo->start_seq_nr = rtpinfo->info_seq_num;
397 statinfo->start_time = current_time;
399 statinfo->jitter = 0;
400 statinfo->flags |= STAT_FLAG_FIRST;
401 statinfo->first_packet = FALSE;
403 /* is it a packet with the mark bit set? */
404 if (rtpinfo->info_marker_set) {
405 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
406 statinfo->flags |= STAT_FLAG_MARKER;
408 /* is it a regular packet? */
409 if (!(statinfo->flags & STAT_FLAG_FIRST)
410 && !(statinfo->flags & STAT_FLAG_MARKER)
411 && !(statinfo->flags & STAT_FLAG_PT_CN)
412 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
413 /* include it in maximum delay calculation */
414 if (statinfo->delay > statinfo->max_delay) {
415 statinfo->max_delay = statinfo->delay;
416 statinfo->max_nr = pinfo->fd->num;
419 /* regular payload change? (CN ignored) */
420 if (!(statinfo->flags & STAT_FLAG_FIRST)
421 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
422 if ((statinfo->pt != statinfo->reg_pt)
423 && (statinfo->reg_pt != PT_UNDEFINED)) {
424 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
428 /* set regular payload*/
429 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
430 statinfo->reg_pt = statinfo->pt;
434 /* When calculating expected rtp packets the seq number can wrap around
435 * so we have to count the number of cycles
436 * Variable cycles counts the wraps around in forwarding connection and
437 * under is flag that indicates where we are
439 * XXX how to determine number of cycles with all possible lost, late
440 * and duplicated packets without any doubt? It seems to me, that
441 * because of all possible combination of late, duplicated or lost
442 * packets, this can only be more or less good approximation
444 * There are some combinations (rare but theoretically possible),
445 * where below code won't work correctly - statistic may be wrong then.
448 /* so if the current sequence number is less than the start one
449 * we assume, that there is another cycle running */
450 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
452 statinfo->under = TRUE;
454 /* what if the start seq nr was 0? Then the above condition will never
455 * be true, so we add another condition. XXX The problem would arise
456 * if one of the packets with seq nr 0 or 65535 would be lost or late */
457 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
458 (statinfo->under == FALSE)){
460 statinfo->under = TRUE;
462 /* the whole round is over, so reset the flag */
463 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
464 statinfo->under = FALSE;
467 /* Since it is difficult to count lost, duplicate or late packets separately,
468 * we would like to know at least how many times the sequence number was not ok */
470 /* if the current seq number equals the last one or if we are here for
471 * the first time, then it is ok, we just store the current one as the last one */
472 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
473 statinfo->seq_num = rtpinfo->info_seq_num;
474 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
475 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
476 statinfo->seq_num = rtpinfo->info_seq_num;
478 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
479 statinfo->seq_num = rtpinfo->info_seq_num;
480 statinfo->sequence++;
481 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
483 /* late or duplicated */
484 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
485 statinfo->sequence++;
486 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
489 statinfo->time = current_time;
490 statinfo->timestamp = rtpinfo->info_timestamp;
491 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
492 statinfo->total_nr++;
498 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
499 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
500 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
501 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
503 /****************************************************************************/
504 /* adds statistics information from the packet to the clist */
505 static int rtp_packet_add_info(GtkCList *clist,
506 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
513 GdkColor color = COLOR_DEFAULT;
515 then = pinfo->fd->abs_secs;
516 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
517 tm_tmp = localtime(&then);
518 snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
521 tm_tmp->tm_year + 1900,
527 if (statinfo->pt == PT_CN) {
528 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
531 else if (statinfo->pt == PT_CN_OLD) {
532 snprintf(status,40,"Comfort noise (PT=19, reserved)");
535 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
536 snprintf(status,40,"Wrong sequence nr.");
539 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
540 snprintf(status,40,"Payload changed to PT=%u", statinfo->pt);
541 color = COLOR_WARNING;
543 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
544 && !(statinfo->flags & STAT_FLAG_FIRST)
545 && !(statinfo->flags & STAT_FLAG_PT_CN)
546 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
547 && !(statinfo->flags & STAT_FLAG_MARKER)) {
548 snprintf(status,40,"Marker missing?");
549 color = COLOR_WARNING;
552 if (statinfo->flags & STAT_FLAG_MARKER) {
553 color = COLOR_WARNING;
555 snprintf(status,40,OK_TEXT);
558 /* is this the first packet we got in this direction? */
559 if (statinfo->flags & STAT_FLAG_FIRST) {
561 pinfo->fd->num, rtpinfo->info_seq_num,
565 rtpinfo->info_marker_set,
566 timeStr, pinfo->fd->pkt_len,
571 pinfo->fd->num, rtpinfo->info_seq_num,
575 rtpinfo->info_marker_set,
576 timeStr, pinfo->fd->pkt_len,
584 /****************************************************************************/
585 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
586 tap_rtp_stat_t *statinfo,
587 packet_info *pinfo, struct _rtp_info *rtpinfo)
593 /* is this the first packet we got in this direction? */
594 if (statinfo->flags & STAT_FLAG_FIRST) {
595 if (saveinfo->fp == NULL) {
596 saveinfo->saved = FALSE;
597 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
600 saveinfo->saved = TRUE;
603 /* save the voice information */
604 /* if there was already an error, we quit */
605 if (saveinfo->saved == FALSE)
608 /* if the captured length and packet length aren't equal, we quit
609 * because there is some information missing */
610 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
611 saveinfo->saved = FALSE;
612 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
616 /* if padding bit is set, but the padding count is bigger
617 * then the whole RTP data - error with padding count */
618 if ( (rtpinfo->info_padding_set != FALSE) &&
619 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
620 saveinfo->saved = FALSE;
621 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
625 /* do we need to insert some silence? */
626 if ((rtpinfo->info_marker_set) &&
627 !(statinfo->flags & STAT_FLAG_FIRST) &&
628 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
629 /* the amount of silence should be the difference between
630 * the last timestamp and the current one minus x
631 * x should equal the amount of information in the last frame
632 * XXX not done yet */
633 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
634 rtpinfo->info_padding_count); i++) {
635 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
636 fwrite(&tmp, 2, 1, saveinfo->fp);
639 fflush(saveinfo->fp);
643 if (rtpinfo->info_payload_type == PT_PCMU) {
644 if (!rtpinfo->info_all_data_present) {
645 /* Not all the data was captured. */
646 saveinfo->saved = FALSE;
647 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
651 /* we put the pointer at the beginning of the RTP
652 * payload, that is, at the beginning of the RTP data
653 * plus the offset of the payload from the beginning
655 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
656 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
657 tmp = (gint16 )ulaw2linear((unsigned char)*data);
658 fwrite(&tmp, 2, 1, saveinfo->fp);
661 fflush(saveinfo->fp);
662 saveinfo->saved = TRUE;
667 else if (rtpinfo->info_payload_type == PT_PCMA) {
668 if (!rtpinfo->info_all_data_present) {
669 /* Not all the data was captured. */
670 saveinfo->saved = FALSE;
671 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
675 /* we put the pointer at the beginning of the RTP
676 * payload, that is, at the beginning of the RTP data
677 * plus the offset of the payload from the beginning
679 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
680 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
681 tmp = (gint16 )alaw2linear((unsigned char)*data);
682 fwrite(&tmp, 2, 1, saveinfo->fp);
685 fflush(saveinfo->fp);
686 saveinfo->saved = TRUE;
689 /* comfort noise? - do nothing */
690 else if (rtpinfo->info_payload_type == PT_CN
691 || rtpinfo->info_payload_type == PT_CN_OLD) {
693 /* unsupported codec or XXX other error */
695 saveinfo->saved = FALSE;
696 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
704 /****************************************************************************/
707 /****************************************************************************/
708 /* XXX just copied from gtk/rpc_stat.c */
709 void protect_thread_critical_region(void);
710 void unprotect_thread_critical_region(void);
713 /****************************************************************************/
714 /* close the dialog window and remove the tap listener */
715 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
717 /* close and remove temporary files */
718 if (user_data->forward.saveinfo.fp != NULL)
719 fclose(user_data->forward.saveinfo.fp);
720 if (user_data->reversed.saveinfo.fp != NULL)
721 fclose(user_data->reversed.saveinfo.fp);
722 /*XXX: test for error **/
723 remove(user_data->f_tempname);
724 remove(user_data->r_tempname);
726 /* destroy save_voice_as window if open */
727 if (user_data->dlg.save_voice_as_w != NULL)
728 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
730 #ifdef USE_CONVERSATION_GRAPH
731 /* destroy graph window if open */
732 if (user_data->dlg.graph_window != NULL)
733 gtk_widget_destroy(user_data->dlg.graph_window);
736 g_free(user_data->dlg.col_arrows_fwd);
737 g_free(user_data->dlg.col_arrows_rev);
742 /****************************************************************************/
743 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
744 GtkNotebookPage *page _U_,
746 user_data_t *user_data _U_)
748 user_data->dlg.selected_clist =
749 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
750 user_data->dlg.selected_row = 0;
753 /****************************************************************************/
754 static void on_clist_select_row(GtkCList *clist _U_,
758 user_data_t *user_data _U_)
760 user_data->dlg.selected_clist = clist;
761 user_data->dlg.selected_row = row;
765 #ifdef USE_CONVERSATION_GRAPH
766 /****************************************************************************/
767 /* when the graph window gets destroyed */
768 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
770 /* note that graph window has been destroyed */
771 user_data->dlg.graph_window = NULL;
774 /****************************************************************************/
775 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
778 GtkCList *clist = NULL;
779 if (vp.fnumber != 0) {
780 clist = GTK_CLIST(user_data->dlg.clist_fwd);
781 row = gtk_clist_find_row_from_data(clist,
782 GUINT_TO_POINTER(vp.fnumber));
784 clist = GTK_CLIST(user_data->dlg.clist_rev);
785 row = gtk_clist_find_row_from_data(clist,
786 GUINT_TO_POINTER(vp.fnumber));
789 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
790 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
791 gtk_clist_select_row(clist, row, 0);
792 gtk_clist_moveto(clist, row, 0, 0.5, 0);
798 /****************************************************************************/
799 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
805 if (user_data->dlg.graph_window != NULL) {
806 /* There's already a graph window; reactivate it. */
807 reactivate_window(user_data->dlg.graph_window);
811 list = g_list_append(list, &(user_data->series_fwd));
812 list = g_list_append(list, &(user_data->series_rev));
814 user_data->series_fwd.color.pixel = 0;
815 user_data->series_fwd.color.red = 0x80ff;
816 user_data->series_fwd.color.green = 0xe0ff;
817 user_data->series_fwd.color.blue = 0xffff;
818 user_data->series_fwd.yvalue = 0.5;
820 user_data->series_rev.color.pixel = 0;
821 user_data->series_rev.color.red = 0x60ff;
822 user_data->series_rev.color.green = 0xc0ff;
823 user_data->series_rev.color.blue = 0xffff;
824 user_data->series_rev.yvalue = -0.5;
826 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
827 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
828 user_data->port_src_fwd,
829 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
830 user_data->port_dst_fwd,
831 user_data->ssrc_fwd);
833 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
834 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
835 user_data->port_src_rev,
836 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
837 user_data->port_dst_rev,
838 user_data->ssrc_rev);
840 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
841 &graph_selection_callback, user_data);
842 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
843 on_destroy_graph, user_data);
845 #endif /*USE_CONVERSATION_GRAPH*/
848 /****************************************************************************/
849 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
853 if (user_data->dlg.selected_clist!=NULL) {
854 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
855 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
856 goto_frame(&cfile, fnumber);
861 static void draw_stat(user_data_t *user_data);
863 /****************************************************************************/
864 /* re-dissects all packets */
865 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
867 gchar filter_text[256];
869 GString *error_string;
871 /* clear the dialog box clists */
872 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
873 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
875 /* try to compile the filter. */
876 strcpy(filter_text,"rtp && ip");
877 if (!dfilter_compile(filter_text, &sfcode)) {
878 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK, dfilter_error_msg);
882 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 ))",
883 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
884 user_data->port_src_fwd,
885 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
886 user_data->port_dst_fwd,
887 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
888 user_data->port_src_rev,
889 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
890 user_data->port_dst_rev
893 /* register tap listener */
894 error_string = register_tap_listener("rtp", user_data, filter_text,
895 rtp_reset, rtp_packet, rtp_draw);
896 if (error_string != NULL) {
897 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK, error_string->str);
898 g_string_free(error_string, TRUE);
902 /* retap all packets */
903 retap_packets(&cfile);
905 /* remove tap listener again */
906 protect_thread_critical_region();
907 remove_tap_listener(user_data);
908 unprotect_thread_critical_region();
910 /* draw statistics info */
911 draw_stat(user_data);
913 gtk_clist_sort(user_data->dlg.clist_fwd);
914 gtk_clist_sort(user_data->dlg.clist_rev);
917 /****************************************************************************/
918 /* on_destroy is automatically called after that */
919 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
921 gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
922 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
925 /****************************************************************************/
926 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
931 if (user_data->dlg.selected_clist==NULL)
934 clist = user_data->dlg.selected_clist;
935 row = user_data->dlg.selected_row + 1;
937 while (gtk_clist_get_text(clist,row,5,&text)) {
938 if (strcmp(text, OK_TEXT) != 0) {
939 gtk_clist_select_row(clist, row, 0);
940 gtk_clist_moveto(clist, row, 0, 0.5, 0);
948 while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
949 if (strcmp(text, OK_TEXT) != 0) {
950 gtk_clist_select_row(clist, row, 0);
951 gtk_clist_moveto(clist, row, 0, 0.5, 0);
958 /****************************************************************************/
959 /* when we want to save the information */
960 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
963 GtkWidget *rev, *forw, *both;
964 user_data_t *user_data;
970 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
972 /* Perhaps the user specified a directory instead of a file.
973 Check whether they did. */
974 if (test_for_directory(g_dest) == EISDIR) {
975 /* It's a directory - set the file selection box to display it. */
976 set_last_open_dir(g_dest);
978 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
982 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
983 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
984 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
985 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
987 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
988 fp = fopen(g_dest, "w");
990 open_failure_alert_box(g_dest, errno, TRUE);
994 if (GTK_TOGGLE_BUTTON(both)->active) {
995 fprintf(fp, "Forward\n");
997 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
998 file_write_error_message(errno), g_dest);
1004 for(j = 0; j < 8; j++) {
1006 fprintf(fp,"%s",titles[j]);
1008 fprintf(fp,",%s",titles[j]);
1013 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1014 file_write_error_message(errno), g_dest);
1018 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
1019 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1020 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1022 fprintf(fp,"%s",columnText);
1024 fprintf(fp,",%s",columnText);
1029 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1030 file_write_error_message(errno), g_dest);
1036 if (fclose(fp) == EOF) {
1037 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1038 file_write_error_message(errno), g_dest);
1043 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1045 if (GTK_TOGGLE_BUTTON(both)->active) {
1046 fp = fopen(g_dest, "a");
1048 open_failure_alert_box(g_dest, errno, TRUE);
1051 fprintf(fp, "\nReverse\n");
1053 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1054 file_write_error_message(errno), g_dest);
1059 fp = fopen(g_dest, "w");
1061 open_failure_alert_box(g_dest, errno, TRUE);
1065 for(j = 0; j < 8; j++) {
1067 fprintf(fp,"%s",titles[j]);
1069 fprintf(fp,",%s",titles[j]);
1074 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1075 file_write_error_message(errno), g_dest);
1079 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1080 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1081 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1083 fprintf(fp,"%s",columnText);
1085 fprintf(fp,",%s",columnText);
1090 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1091 file_write_error_message(errno), g_dest);
1096 if (fclose(fp) == EOF) {
1097 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1098 file_write_error_message(errno), g_dest);
1103 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1106 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1108 user_data->dlg.save_csv_as_w = NULL;
1111 /* when the user wants to save the csv information in a file */
1112 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
1116 GtkWidget *label_format;
1117 GtkWidget *channels_label;
1118 GSList *channels_group = NULL;
1119 GtkWidget *forward_rb;
1120 GtkWidget *reversed_rb;
1124 if (user_data->dlg.save_csv_as_w != NULL) {
1125 /* There's already a Save CSV info dialog box; reactivate it. */
1126 reactivate_window(user_data->dlg.save_csv_as_w);
1130 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
1131 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
1132 save_csv_as_destroy_cb, user_data);
1134 /* Container for each row of widgets */
1135 vertb = gtk_vbox_new(FALSE, 0);
1136 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1137 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
1138 vertb, FALSE, FALSE, 0);
1139 gtk_widget_show (vertb);
1141 table1 = gtk_table_new (2, 4, FALSE);
1142 gtk_widget_show (table1);
1143 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1144 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1145 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1147 label_format = gtk_label_new ("Format: Comma Separated Values");
1148 gtk_widget_show (label_format);
1149 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1150 (GtkAttachOptions) (GTK_FILL),
1151 (GtkAttachOptions) (0), 0, 0);
1154 channels_label = gtk_label_new ("Channels:");
1155 gtk_widget_show (channels_label);
1156 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1157 (GtkAttachOptions) (GTK_FILL),
1158 (GtkAttachOptions) (0), 0, 0);
1159 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1161 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1162 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1163 gtk_widget_show (forward_rb);
1164 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1165 (GtkAttachOptions) (GTK_FILL),
1166 (GtkAttachOptions) (0), 0, 0);
1168 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1169 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1170 gtk_widget_show (reversed_rb);
1171 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1172 (GtkAttachOptions) (GTK_FILL),
1173 (GtkAttachOptions) (0), 0, 0);
1175 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1176 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1177 gtk_widget_show (both_rb);
1178 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1179 (GtkAttachOptions) (GTK_FILL),
1180 (GtkAttachOptions) (0), 0, 0);
1182 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1184 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1185 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1186 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1187 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1188 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1190 /* Connect the cancel_button to destroy the widget */
1191 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1192 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1193 user_data->dlg.save_csv_as_w);
1195 /* Catch the "key_press_event" signal in the window, so that we can catch
1196 the ESC key being pressed and act as if the "Cancel" button had
1198 dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1200 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
1201 user_data->dlg.save_csv_as_w);
1203 gtk_widget_show(user_data->dlg.save_csv_as_w);
1207 /****************************************************************************/
1208 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1210 /* Note that we no longer have a Save voice info dialog box. */
1211 user_data->dlg.save_voice_as_w = NULL;
1214 /****************************************************************************/
1215 /* here we save it into a file that user specified */
1216 /* XXX what about endians here? could go something wrong? */
1217 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1219 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1223 guint32 f_write_silence = 0;
1224 guint32 r_write_silence = 0;
1226 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1227 gboolean stop_flag = FALSE;
1229 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1232 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1238 /* open file for saving */
1239 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1246 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
1248 /* First we write the .au header. XXX Hope this is endian independant */
1249 /* the magic word 0x2e736e64 == .snd */
1250 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1251 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1252 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1253 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1254 /* header offset == 24 bytes */
1255 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1256 write(to_fd, pd, 1);
1257 write(to_fd, pd, 1);
1258 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1259 /* total length, it is permited to set this to 0xffffffff */
1260 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
1261 write(to_fd, pd, 1);
1262 write(to_fd, pd, 1);
1263 write(to_fd, pd, 1);
1264 /* encoding format == 8 bit ulaw */
1265 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1266 write(to_fd, pd, 1);
1267 write(to_fd, pd, 1);
1268 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1269 /* sample rate == 8000 Hz */
1270 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1271 write(to_fd, pd, 1);
1272 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1273 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1275 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1276 write(to_fd, pd, 1);
1277 write(to_fd, pd, 1);
1278 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1281 /* only forward direction */
1283 progbar_count = user_data->forward.saveinfo.count;
1284 progbar_quantum = user_data->forward.saveinfo.count/100;
1285 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1288 if((count > progbar_nextstep) && (count <= progbar_count)) {
1289 update_progress_dlg(progbar,
1290 (gfloat) count/progbar_count, "Saving");
1291 progbar_nextstep = progbar_nextstep + progbar_quantum;
1294 *pd = (unsigned char)linear2ulaw(f_pd);
1295 fwritten = write(to_fd, pd, 1);
1296 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1300 destroy_progress_dlg(progbar);
1306 /* only reversed direction */
1308 progbar_count = user_data->reversed.saveinfo.count;
1309 progbar_quantum = user_data->reversed.saveinfo.count/100;
1310 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1313 if((count > progbar_nextstep) && (count <= progbar_count)) {
1314 update_progress_dlg(progbar,
1315 (gfloat) count/progbar_count, "Saving");
1316 progbar_nextstep = progbar_nextstep + progbar_quantum;
1319 *pd = (unsigned char)linear2ulaw(r_pd);
1320 rwritten = write(to_fd, pd, 1);
1321 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1325 destroy_progress_dlg(progbar);
1331 /* both directions */
1333 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
1334 (progbar_count = user_data->forward.saveinfo.count) :
1335 (progbar_count = user_data->reversed.saveinfo.count);
1336 progbar_quantum = progbar_count/100;
1337 /* since conversation in one way can start later than in the other one,
1338 * we have to write some silence information for one channel */
1339 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1340 f_write_silence = (guint32)
1341 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
1343 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1344 r_write_silence = (guint32)
1345 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
1350 if((count > progbar_nextstep) && (count <= progbar_count)) {
1351 update_progress_dlg(progbar,
1352 (gfloat) count/progbar_count, "Saving");
1353 progbar_nextstep = progbar_nextstep + progbar_quantum;
1356 if(f_write_silence > 0) {
1357 rread = read(rev_fd, &r_pd, 2);
1362 else if(r_write_silence > 0) {
1363 fread = read(forw_fd, &f_pd, 2);
1369 fread = read(forw_fd, &f_pd, 2);
1370 rread = read(rev_fd, &r_pd, 2);
1372 if ((rread == 0) && (fread == 0))
1374 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1375 rwritten = write(to_fd, pd, 1);
1376 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1380 destroy_progress_dlg(progbar);
1386 destroy_progress_dlg(progbar);
1394 /****************************************************************************/
1395 /* the user wants to save in a file */
1396 /* XXX support for different formats is currently commented out */
1397 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1400 /*GtkWidget *wav, *au, *sw;*/
1401 GtkWidget *rev, *forw, *both;
1402 user_data_t *user_data;
1403 gint channels /*, format*/;
1405 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1407 /* Perhaps the user specified a directory instead of a file.
1408 Check whether they did. */
1409 if (test_for_directory(g_dest) == EISDIR) {
1410 /* It's a directory - set the file selection box to display it. */
1411 set_last_open_dir(g_dest);
1413 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1417 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1418 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1419 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1420 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1421 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1422 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1423 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1425 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1426 * we don't support that codec. So we pop up a warning. Maybe it would be better to
1427 * disable the ok button or disable the buttons for direction if only one is not ok. The
1428 * problem is if we open the save voice dialog and then click the refresh button and maybe
1429 * the state changes, so we can't save anymore. In this case we should be able to update
1430 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1433 /* we can not save in both dirctions */
1434 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1435 /* there are many combinations here, we just exit when first matches */
1436 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
1437 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1438 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1439 "Can't save in a file: Unsupported codec!");
1440 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
1441 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1442 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1443 "Can't save in a file: Wrong length of captured packets!");
1444 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
1445 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1446 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1447 "Can't save in a file: RTP data with padding!");
1448 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
1449 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
1450 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1451 "Can't save in a file: Not all data in all packets was captured!");
1453 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1454 "Can't save in a file: File I/O problem!");
1457 /* we can not save forward direction */
1458 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1459 (GTK_TOGGLE_BUTTON (both)->active))) {
1460 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1461 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1462 "Can't save forward direction in a file: Unsupported codec!");
1463 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1464 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1465 "Can't save forward direction in a file: Wrong length of captured packets!");
1466 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1467 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1468 "Can't save forward direction in a file: RTP data with padding!");
1469 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
1470 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1471 "Can't save forward direction in a file: Not all data in all packets was captured!");
1473 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1474 "Can't save forward direction in a file: File I/O problem!");
1477 /* we can not save reversed direction */
1478 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1479 (GTK_TOGGLE_BUTTON (both)->active))) {
1480 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1481 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1482 "Can't save reversed direction in a file: Unsupported codec!");
1483 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1484 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1485 "Can't save reversed direction in a file: Wrong length of captured packets!");
1486 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1487 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1488 "Can't save reversed direction in a file: RTP data with padding!");
1489 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
1490 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1491 "Can't save reversed direction in a file: Not all data in all packets was captured!");
1492 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1493 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1494 "Can't save reversed direction in a file: No RTP data!");
1496 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1497 "Can't save reversed direction in a file: File I/O problem!");
1501 /*if (GTK_TOGGLE_BUTTON (wav)->active)
1503 else if (GTK_TOGGLE_BUTTON (au)->active)
1505 else if (GTK_TOGGLE_BUTTON (sw)->active)
1508 if (GTK_TOGGLE_BUTTON (rev)->active)
1510 else if (GTK_TOGGLE_BUTTON (both)->active)
1515 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1516 /* XXX - report the error type! */
1517 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1518 "An error occured while saving voice in a file!");
1522 gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1525 /****************************************************************************/
1526 /* when the user wants to save the voice information in a file */
1527 /* XXX support for different formats is currently commented out */
1528 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1532 GtkWidget *label_format;
1533 GtkWidget *channels_label;
1534 /*GSList *format_group = NULL;*/
1535 GSList *channels_group = NULL;
1536 GtkWidget *forward_rb;
1537 GtkWidget *reversed_rb;
1539 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1542 /* if we can't save in a file: wrong codec, cut packets or other errors */
1543 /* shold the error arise here or later when you click ok button ?
1544 * if we do it here, then we must disable the refresh button, so we don't do it here */
1546 if (user_data->dlg.save_voice_as_w != NULL) {
1547 /* There's already a Save voice info dialog box; reactivate it. */
1548 reactivate_window(user_data->dlg.save_voice_as_w);
1552 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1553 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
1554 save_voice_as_destroy_cb, user_data);
1556 /* Container for each row of widgets */
1557 vertb = gtk_vbox_new(FALSE, 0);
1558 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1559 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1560 vertb, FALSE, FALSE, 0);
1561 gtk_widget_show (vertb);
1563 table1 = gtk_table_new (2, 4, FALSE);
1564 gtk_widget_show (table1);
1565 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1566 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1567 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1569 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1570 gtk_widget_show (label_format);
1571 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1572 (GtkAttachOptions) (GTK_FILL),
1573 (GtkAttachOptions) (0), 0, 0);
1575 /* we support .au - ulaw*/
1576 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1577 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1578 gtk_widget_show (wav_rb);
1579 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1580 (GtkAttachOptions) (GTK_FILL),
1581 (GtkAttachOptions) (0), 0, 0);
1583 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
1584 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1585 gtk_widget_show (sw_rb);
1586 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1587 (GtkAttachOptions) (GTK_FILL),
1588 (GtkAttachOptions) (0), 0, 0);
1589 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1590 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1591 gtk_widget_show (au_rb);
1592 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1593 (GtkAttachOptions) (GTK_FILL),
1594 (GtkAttachOptions) (0), 0, 0);
1597 channels_label = gtk_label_new ("Channels:");
1598 gtk_widget_show (channels_label);
1599 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1600 (GtkAttachOptions) (GTK_FILL),
1601 (GtkAttachOptions) (0), 0, 0);
1602 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1604 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
1605 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1606 gtk_widget_show (forward_rb);
1607 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1608 (GtkAttachOptions) (GTK_FILL),
1609 (GtkAttachOptions) (0), 0, 0);
1611 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1612 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1613 gtk_widget_show (reversed_rb);
1614 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1615 (GtkAttachOptions) (GTK_FILL),
1616 (GtkAttachOptions) (0), 0, 0);
1618 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1619 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1620 gtk_widget_show (both_rb);
1621 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1622 (GtkAttachOptions) (GTK_FILL),
1623 (GtkAttachOptions) (0), 0, 0);
1625 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1627 /* if one direction is nok we don't allow saving
1628 XXX this is not ok since the user can click the refresh button and cause changes
1629 but we can not update this window. So we move all the decision on the time the ok
1631 if (user_data->forward.saved == FALSE) {
1632 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1633 gtk_widget_set_sensitive(forward_rb, FALSE);
1634 gtk_widget_set_sensitive(both_rb, FALSE);
1636 else if (user_data->reversed.saved == FALSE) {
1637 gtk_widget_set_sensitive(reversed_rb, FALSE);
1638 gtk_widget_set_sensitive(both_rb, FALSE);
1642 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1643 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1644 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1645 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1646 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1647 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1648 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1649 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1651 /* Connect the cancel_button to destroy the widget */
1652 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1653 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1654 user_data->dlg.save_voice_as_w);
1656 /* Catch the "key_press_event" signal in the window, so that we can catch
1657 the ESC key being pressed and act as if the "Cancel" button had
1659 dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1661 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
1662 user_data->dlg.save_voice_as_w);
1664 gtk_widget_show(user_data->dlg.save_voice_as_w);
1668 /****************************************************************************/
1669 /* when we are finished with redisection, we add the label for the statistic */
1670 static void draw_stat(user_data_t *user_data)
1672 gchar label_max[200];
1673 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1674 - user_data->forward.statinfo.start_seq_nr + 1;
1675 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1676 - user_data->reversed.statinfo.start_seq_nr + 1;
1677 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1678 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1680 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1681 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1682 " Sequence errors = %u",
1683 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr,
1684 user_data->forward.statinfo.total_nr,
1685 f_expected, f_lost, user_data->forward.statinfo.sequence);
1687 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1689 g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1690 "Total RTP packets = %u (expected %u) Lost RTP packets = %d"
1691 " Sequence errors = %u",
1692 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr,
1693 user_data->reversed.statinfo.total_nr,
1694 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1696 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1704 /****************************************************************************/
1705 /* append a line to clist */
1706 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1707 double delay, double jitter, gchar *status, gboolean marker,
1708 gchar *timeStr, guint32 pkt_len, GdkColor *color)
1714 data[0]=&field[0][0];
1715 data[1]=&field[1][0];
1716 data[2]=&field[2][0];
1717 data[3]=&field[3][0];
1718 data[4]=&field[4][0];
1719 data[5]=&field[5][0];
1720 data[6]=&field[6][0];
1721 data[7]=&field[7][0];
1723 g_snprintf(field[0], 20, "%u", number);
1724 g_snprintf(field[1], 20, "%u", seq_num);
1725 g_snprintf(field[2], 20, "%f", delay);
1726 g_snprintf(field[3], 20, "%f", jitter);
1727 g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1728 g_snprintf(field[5], 40, "%s", status);
1729 g_snprintf(field[6], 32, "%s", timeStr);
1730 g_snprintf(field[7], 20, "%u", pkt_len);
1732 added_row = gtk_clist_append(GTK_CLIST(clist), data);
1733 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1734 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1738 /****************************************************************************/
1739 /* callback for sorting columns of clist */
1740 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
1747 const GtkCListRow *row1 = (GtkCListRow *) ptr1;
1748 const GtkCListRow *row2 = (GtkCListRow *) ptr2;
1750 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
1751 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
1753 switch(clist->sort_column){
1754 /* columns representing strings */
1758 return strcmp (text1, text2);
1759 /* columns representing ints */
1766 /* columns representing floats */
1771 if (fabs(f1-f2)<0.0000005)
1777 g_assert_not_reached();
1782 /****************************************************************************/
1784 click_column_cb(GtkCList *clist, gint column, gpointer data)
1786 column_arrows *col_arrows = (column_arrows *) data;
1789 gtk_clist_freeze(clist);
1791 for (i = 0; i < NUM_COLS; i++) {
1792 gtk_widget_hide(col_arrows[i].ascend_pm);
1793 gtk_widget_hide(col_arrows[i].descend_pm);
1796 if (column == clist->sort_column) {
1797 if (clist->sort_type == GTK_SORT_ASCENDING) {
1798 clist->sort_type = GTK_SORT_DESCENDING;
1799 gtk_widget_show(col_arrows[column].descend_pm);
1801 clist->sort_type = GTK_SORT_ASCENDING;
1802 gtk_widget_show(col_arrows[column].ascend_pm);
1805 clist->sort_type = GTK_SORT_ASCENDING;
1806 gtk_widget_show(col_arrows[column].ascend_pm);
1807 gtk_clist_set_sort_column(clist, column);
1809 gtk_clist_thaw(clist);
1811 gtk_clist_sort(clist);
1815 /****************************************************************************/
1816 /* Add the packet list */
1818 GtkWidget* create_clist(user_data_t* user_data)
1820 GtkWidget* clist_fwd;
1822 /* clist for the information */
1823 clist_fwd = gtk_clist_new(8);
1824 gtk_widget_show(clist_fwd);
1825 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
1827 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
1828 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
1829 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
1830 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
1832 /* hide date and length column */
1833 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1834 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1836 /* column widths and justification */
1837 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
1838 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
1839 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
1840 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
1841 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
1842 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
1843 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
1844 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1845 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1846 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1847 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1853 /****************************************************************************/
1854 /* Add the sort by column feature for a packet clist */
1856 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
1857 user_data_t* user_data _U_)
1859 column_arrows *col_arrows;
1860 GdkBitmap *ascend_bm, *descend_bm;
1861 GdkPixmap *ascend_pm, *descend_pm;
1862 GtkStyle *win_style;
1863 GtkWidget *column_lb;
1866 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
1867 win_style = gtk_widget_get_style(window);
1868 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1870 &win_style->bg[GTK_STATE_NORMAL],
1871 (gchar **)clist_ascend_xpm);
1872 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1874 &win_style->bg[GTK_STATE_NORMAL],
1875 (gchar **)clist_descend_xpm);
1877 for (i=0; i<NUM_COLS; i++) {
1878 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1879 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1880 column_lb = gtk_label_new(titles[i]);
1881 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1882 gtk_widget_show(column_lb);
1884 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1885 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1886 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1887 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1888 /* make packet-nr be the default sort order */
1890 gtk_widget_show(col_arrows[i].ascend_pm);
1892 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
1893 gtk_widget_show(col_arrows[i].table);
1896 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
1901 /****************************************************************************/
1902 /* Create the dialog box with all widgets */
1903 void create_rtp_dialog(user_data_t* user_data)
1905 GtkWidget *window = NULL;
1906 GtkWidget *clist_fwd;
1907 GtkWidget *clist_rev;
1908 GtkWidget *label_stats_fwd;
1909 GtkWidget *label_stats_rev;
1910 GtkWidget *notebook;
1912 GtkWidget *main_vb, *page, *page_r;
1914 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1915 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1916 #ifdef USE_CONVERSATION_GRAPH
1917 GtkWidget *graph_bt;
1920 gchar label_forward[150];
1921 gchar label_reverse[150];
1923 gchar str_ip_src[16];
1924 gchar str_ip_dst[16];
1925 column_arrows *col_arrows_fwd;
1926 column_arrows *col_arrows_rev;
1929 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1930 gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1931 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1932 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
1934 /* Container for each row of widgets */
1935 main_vb = gtk_vbox_new(FALSE, 2);
1936 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
1937 gtk_container_add(GTK_CONTAINER(window), main_vb);
1938 gtk_widget_show(main_vb);
1941 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1942 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1944 g_snprintf(label_forward, 149,
1945 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1946 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1948 strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1949 strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1951 g_snprintf(label_reverse, 149,
1952 "Analysing stream from %s port %u to %s port %u SSRC = %u",
1953 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1955 /* Start a notebook for flipping between sets of changes */
1956 notebook = gtk_notebook_new();
1957 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1958 OBJECT_SET_DATA(window, "notebook", notebook);
1959 SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
1962 /* page for forward connection */
1963 page = gtk_vbox_new(FALSE, 8);
1964 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1966 /* direction label */
1967 label = gtk_label_new(label_forward);
1968 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
1970 /* place for some statistics */
1971 label_stats_fwd = gtk_label_new("\n");
1972 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
1974 /* scrolled window */
1975 scrolled_window = scrolled_window_new(NULL, NULL);
1976 WIDGET_SET_SIZE(scrolled_window, 560, 200);
1979 clist_fwd = create_clist(user_data);
1980 gtk_widget_show(clist_fwd);
1981 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1982 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1983 gtk_widget_show(scrolled_window);
1986 label = gtk_label_new(" Forward Direction ");
1987 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1989 /* same page for reversed connection */
1990 page_r = gtk_vbox_new(FALSE, 8);
1991 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
1992 label = gtk_label_new(label_reverse);
1993 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
1994 label_stats_rev = gtk_label_new("\n");
1995 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
1997 scrolled_window_r = scrolled_window_new(NULL, NULL);
1998 WIDGET_SET_SIZE(scrolled_window_r, 560, 200);
2000 clist_rev = create_clist(user_data);
2001 gtk_widget_show(clist_rev);
2002 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
2003 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
2004 gtk_widget_show(scrolled_window_r);
2006 label = gtk_label_new(" Reversed Direction ");
2007 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
2009 /* page for help&about or future
2010 page_help = gtk_hbox_new(FALSE, 5);
2011 label = gtk_label_new(" Future ");
2012 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
2013 frame = gtk_frame_new("");
2014 text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
2015 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
2016 gtk_container_add(GTK_CONTAINER(frame), text);
2017 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
2018 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
2021 /* show all notebooks */
2022 gtk_widget_show_all(notebook);
2025 box4 = gtk_hbutton_box_new();
2026 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
2027 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
2028 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
2029 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
2030 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
2031 gtk_widget_show(box4);
2033 voice_bt = gtk_button_new_with_label("Save payload...");
2034 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
2035 gtk_widget_show(voice_bt);
2036 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
2038 csv_bt = gtk_button_new_with_label("Save as CSV...");
2039 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
2040 gtk_widget_show(csv_bt);
2041 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
2043 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
2044 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
2045 gtk_widget_show(refresh_bt);
2046 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
2048 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
2049 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
2050 gtk_widget_show(goto_bt);
2051 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
2053 #ifdef USE_CONVERSATION_GRAPH
2054 graph_bt = gtk_button_new_with_label("Graph");
2055 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
2056 gtk_widget_show(graph_bt);
2057 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
2060 next_bt = gtk_button_new_with_label("Next non-Ok");
2061 gtk_container_add(GTK_CONTAINER(box4), next_bt);
2062 gtk_widget_show(next_bt);
2063 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
2065 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
2066 gtk_container_add(GTK_CONTAINER(box4), close_bt);
2067 gtk_widget_show(close_bt);
2068 SIGNAL_CONNECT(close_bt, "clicked", on_close_bt_clicked, user_data);
2070 gtk_widget_show(window);
2072 /* sort by column feature */
2073 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
2074 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
2076 /* some widget references need to be saved for outside use */
2077 user_data->dlg.window = window;
2078 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
2079 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
2080 user_data->dlg.label_stats_fwd = label_stats_fwd;
2081 user_data->dlg.label_stats_rev = label_stats_rev;
2082 user_data->dlg.notebook = notebook;
2083 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
2084 user_data->dlg.selected_row = 0;
2085 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
2086 user_data->dlg.col_arrows_rev = col_arrows_rev;
2090 /****************************************************************************/
2091 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
2092 gchar* proto_field, guint32* p_result)
2095 proto_node *proto_sibling_node;
2096 header_field_info *hfssrc;
2099 finfo = PITEM_FINFO(ptree_node);
2101 if (hfinformation==(finfo->hfinfo)) {
2102 hfssrc = proto_registrar_get_byname(proto_field);
2105 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
2106 ptree_node=ptree_node->next) {
2107 finfo=PITEM_FINFO(ptree_node);
2108 if (hfssrc==finfo->hfinfo) {
2109 if (hfinformation->type==FT_IPv4) {
2110 ipv4 = fvalue_get(&finfo->value);
2111 *p_result = ipv4_get_net_order_addr(ipv4);
2114 *p_result = fvalue_get_integer(&finfo->value);
2121 proto_sibling_node = ptree_node->next;
2123 if (proto_sibling_node) {
2124 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
2130 /****************************************************************************/
2131 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
2136 proto_node *ptree_node;
2137 header_field_info *hfinformation;
2139 hfinformation = proto_registrar_get_byname(proto_name);
2140 if (hfinformation == NULL)
2143 ptree_node = ((proto_node *)protocol_tree)->first_child;
2147 return process_node(ptree_node, hfinformation, proto_field, p_result);
2151 /* XXX just copied from gtk/rpc_stat.c */
2152 void protect_thread_critical_region(void);
2153 void unprotect_thread_critical_region(void);
2155 /****************************************************************************/
2156 /* XXX only handles RTP over IPv4, should add IPv6 support */
2159 guint16 port_src_fwd,
2161 guint16 port_dst_fwd,
2164 guint16 port_src_rev,
2166 guint16 port_dst_rev,
2170 user_data_t *user_data;
2174 user_data = g_malloc(sizeof(user_data_t));
2176 user_data->ip_src_fwd = ip_src_fwd;
2177 user_data->port_src_fwd = port_src_fwd;
2178 user_data->ip_dst_fwd = ip_dst_fwd;
2179 user_data->port_dst_fwd = port_dst_fwd;
2180 user_data->ssrc_fwd = ssrc_fwd;
2181 user_data->ip_src_rev = ip_src_rev;
2182 user_data->port_src_rev = port_src_rev;
2183 user_data->ip_dst_rev = ip_dst_rev;
2184 user_data->port_dst_rev = port_dst_rev;
2185 user_data->ssrc_rev = ssrc_rev;
2187 /* file names for storing sound data */
2188 /*XXX: check for errors*/
2189 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
2192 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
2195 user_data->forward.saveinfo.fp = NULL;
2196 user_data->reversed.saveinfo.fp = NULL;
2197 user_data->dlg.save_voice_as_w = NULL;
2198 user_data->dlg.save_csv_as_w = NULL;
2199 #ifdef USE_CONVERSATION_GRAPH
2200 user_data->dlg.graph_window = NULL;
2201 user_data->series_fwd.value_pairs = NULL;
2202 user_data->series_rev.value_pairs = NULL;
2205 /* create the dialog box */
2206 create_rtp_dialog(user_data);
2208 /* proceed as if the Refresh button would have been pressed */
2209 on_refresh_bt_clicked(NULL, user_data);
2212 /****************************************************************************/
2213 /* entry point from main menu */
2214 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
2217 guint16 port_src_fwd;
2219 guint16 port_dst_fwd;
2220 guint32 ssrc_fwd = 0;
2222 guint16 port_src_rev;
2224 guint16 port_dst_rev;
2225 guint32 ssrc_rev = 0;
2227 gchar filter_text[256];
2230 epan_dissect_t *edt;
2233 gboolean frame_matched;
2235 GList *strinfo_list;
2236 GList *filtered_list = NULL;
2237 rtp_stream_info_t *strinfo;
2240 /* Try to compile the filter. */
2241 strcpy(filter_text,"rtp && ip");
2242 if (!dfilter_compile(filter_text, &sfcode)) {
2243 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2247 /* we load the current file into cf variable */
2249 fdata = cf->current_frame;
2251 /* we are on the selected frame now */
2253 return; /* if we exit here it's an error */
2255 /* dissect the current frame */
2256 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
2257 cf->pd, fdata->cap_len, &err, &err_info)) {
2258 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2259 cf_read_error_message(err, err_info), cf->filename);
2262 edt = epan_dissect_new(TRUE, FALSE);
2263 epan_dissect_prime_dfilter(edt, sfcode);
2264 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
2265 frame_matched = dfilter_apply_edt(sfcode, edt);
2267 /* if it is not an rtp frame, show the rtpstream dialog */
2268 frame_matched = dfilter_apply_edt(sfcode, edt);
2269 if (frame_matched != 1) {
2270 epan_dissect_free(edt);
2271 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2272 "You didn't choose a RTP packet!");
2276 /* ok, it is a RTP frame, so let's get the ip and port values */
2277 g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
2278 g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
2279 port_src_fwd = edt->pi.srcport;
2280 port_dst_fwd = edt->pi.destport;
2282 /* assume the inverse ip/port combination for the reverse direction */
2283 g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
2284 g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
2285 port_src_rev = edt->pi.destport;
2286 port_dst_rev = edt->pi.srcport;
2288 /* now we need the SSRC value of the current frame */
2289 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
2290 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2291 "SSRC value couldn't be found!");
2295 /* search for reversed direction in the global rtp streams list */
2297 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
2298 while (strinfo_list)
2300 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
2301 if (strinfo->src_addr==ip_src_fwd
2302 && strinfo->src_port==port_src_fwd
2303 && strinfo->dest_addr==ip_dst_fwd
2304 && strinfo->dest_port==port_dst_fwd)
2306 filtered_list = g_list_prepend(filtered_list, strinfo);
2309 if (strinfo->src_addr==ip_src_rev
2310 && strinfo->src_port==port_src_rev
2311 && strinfo->dest_addr==ip_dst_rev
2312 && strinfo->dest_port==port_dst_rev)
2315 filtered_list = g_list_append(filtered_list, strinfo);
2317 ssrc_rev = strinfo->ssrc;
2320 strinfo_list = g_list_next(strinfo_list);
2323 /* if more than one reverse streams found, we let the user choose the right one */
2325 rtpstream_dlg_show(filtered_list);
2344 /****************************************************************************/
2346 rtp_analysis_init(char *dummy _U_)
2348 rtp_analysis_cb(NULL, NULL);
2351 /****************************************************************************/
2353 register_tap_listener_rtp_analysis(void)
2355 register_ethereal_tap("rtp", rtp_analysis_init);
2359 register_tap_menu_rtp_analysis(void)
2361 register_tap_menu_item("_Statistics/RTP Streams/Analyse...",
2362 rtp_analysis_cb, NULL, NULL, NULL);