From Lars Ruoff: rewritten RTP analysis module.
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 24 Sep 2003 07:48:12 +0000 (07:48 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Wed, 24 Sep 2003 07:48:12 +0000 (07:48 +0000)
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@8529 f5534014-38df-0310-8fa8-9805f1628bb7

AUTHORS
doc/ethereal.pod.template
gtk/Makefile.am
gtk/Makefile.nmake
gtk/rtp_analysis.c [new file with mode: 0644]
gtk/rtp_analysis.h [new file with mode: 0644]
gtk/rtp_stream.c [new file with mode: 0644]
gtk/rtp_stream.h [new file with mode: 0644]
gtk/rtp_stream_dlg.c [new file with mode: 0644]
gtk/rtp_stream_dlg.h [new file with mode: 0644]
gtk/tap_rtp.c [deleted file]

diff --git a/AUTHORS b/AUTHORS
index 303333dd2424b7759caafb7427d7b87910ea1f18..39bd9ff328459dc0c8884384c802fedd5614b955 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1678,6 +1678,7 @@ Lars Roland <Lars.Roland [AT] gmx.net> {
 Miha Jemec <m.jemec [AT] iskratel.si> {
        Support to follow a RTP stream and save it as a file.
        Support for G.711 codec
+       Original RTP analysis tap
 }
 
 Markus Friedl <markus [AT] openbsd.org> {
@@ -1853,6 +1854,10 @@ Graeme Reid <graeme.reid [AT] norwoodsystems.com> {
        H.450 support
 }
 
+Lars Ruoff <lars.ruoff [AT] sxb.bsf.alcatel.fr> {
+       Rewritten RTP analysis tap
+}
+
 And assorted fixes and enhancements by the people listed above and by:
 
        Pavel Roskin <proski [AT] gnu.org>
index c3ddd7e4cdf4e63c6d56b054a0f597677c69390d..1b53e660ab5b93255a60e73be7c31a77358bfbad 100644 (file)
@@ -2101,6 +2101,7 @@ B<http://www.ethereal.com>.
   Kevin A. Noll            <knoll [AT] poss.com>
   Emanuele Caratti         <wiz [AT] libero.it>
   Graeme Reid              <graeme.reid [AT] norwoodsystems.com>
+  Lars Ruoff               <lars.ruoff [AT] sxb.bsf.alcatel.fr>
   Pavel Roskin             <proski [AT] gnu.org>
   Georgi Guninski          <guninski [AT] guninski.com>
   Jason Copenhaver         <jcopenha [AT] typedef.org>
index 1935b851d668758662b17da697f141694566f96f..c8cdd6fe00e8e2756b570fe49d54e33720c28d4c 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile.am
 # Automake file for the GTK interface routines for Ethereal
 #
-# $Id: Makefile.am,v 1.69 2003/09/24 06:15:51 oabad Exp $
+# $Id: Makefile.am,v 1.70 2003/09/24 07:48:10 guy Exp $
 #
 # Ethereal - Network traffic analyzer
 # By Gerald Combs <gerald@ethereal.com>
@@ -45,7 +45,9 @@ ETHEREAL_TAP_SRC = \
        rpc_stat.c      \
        rpc_progs.c     \
        smb_stat.c      \
-       tap_rtp.c       \
+       rtp_analysis.c  \
+       rtp_stream.c    \
+       rtp_stream_dlg.c \
        wsp_stat.c
 
 ethereal-tap-register.c: $(ETHEREAL_TAP_SRC) $(top_srcdir)/make-tapreg-dotc
@@ -122,9 +124,12 @@ libui_a_SOURCES = \
        proto_draw.h    \
        proto_hier_stats_dlg.h  \
        proto_hier_stats_dlg.c  \
-       simple_dialog.c \
+       rtp_analysis.h  \
+       rtp_stream.h    \
+       rtp_stream_dlg.h \
        service_response_time_table.c   \
        service_response_time_table.h   \
+       simple_dialog.c \
        stream_prefs.c  \
        stream_prefs.h  \
        summary_dlg.c   \
@@ -205,6 +210,9 @@ libui_a_SOURCES = \
        proto_draw.h    \
        proto_hier_stats_dlg.h  \
        proto_hier_stats_dlg.c  \
+       rtp_analysis.h  \
+       rtp_stream.h    \
+       rtp_stream_dlg.h \
        service_response_time_table.c   \
        service_response_time_table.h   \
        simple_dialog.c \
index 28787f808176ef36380c638fd8e00cba4ce80c14..878eabcefb085c2298ac3e30b98a7d6ebfb59e9b 100644 (file)
@@ -1,7 +1,7 @@
 ## Makefile for building ethereal.exe with Microsoft C and nmake
 ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake
 #
-# $Id: Makefile.nmake,v 1.52 2003/09/10 05:35:25 guy Exp $
+# $Id: Makefile.nmake,v 1.53 2003/09/24 07:48:11 guy Exp $
 
 include ..\config.nmake
 
@@ -37,7 +37,9 @@ ETHEREAL_TAP_SRC = \
        rpc_stat.c      \
        rpc_progs.c     \
        smb_stat.c      \
-       tap_rtp.c       \
+       rtp_analysis.c  \
+       rtp_stream.c    \
+       rtp_stream_dlg.c \
        wsp_stat.c
 
 ETHEREAL_TAP_OBJECTS = $(ETHEREAL_TAP_SRC:.c=.obj)
diff --git a/gtk/rtp_analysis.c b/gtk/rtp_analysis.c
new file mode 100644 (file)
index 0000000..95f20db
--- /dev/null
@@ -0,0 +1,1895 @@
+/* rtp_analysis.c
+ * RTP analysis addition for ethereal
+ *
+ * $Id: rtp_analysis.c,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*do not define this symbol. will be added soon*/
+/*#define USE_CONVERSATION_GRAPH 1*/
+
+#include "rtp_analysis.h"
+#include "rtp_stream.h"
+#include "rtp_stream_dlg.h"
+
+#ifdef USE_CONVERSATION_GRAPH
+#include "../graph/graph.h"
+#endif
+
+#include "epan/epan_dissect.h"
+#include "epan/filesystem.h"
+#include "tap.h"
+#include "register.h"
+#include "packet-rtp.h"
+#include "g711.h"
+
+/* in /gtk ... */
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "simple_dialog.h"
+#include "menu.h"
+#include "main.h"
+#include "progress_dlg.h"
+#include "compat_macros.h"
+
+#include <math.h>
+#include <fcntl.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h> /* open/close on win32 */
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/****************************************************************************/
+
+typedef struct _dialog_data_t {
+       GtkWidget *window;
+       GtkWidget *clist_fwd;
+       GtkWidget *clist_rev;
+       GtkWidget *label_stats_fwd;
+       GtkWidget *label_stats_rev;
+       GtkWidget *notebook;
+       GtkCList *selected_clist;
+       GtkWidget *save_voice_as_w;
+       GtkWidget *save_csv_as_w;
+       gint selected_row;
+#ifdef USE_CONVERSATION_GRAPH
+       GtkWidget *graph_window;
+#endif
+} dialog_data_t;
+
+/* type of error when saving voice in a file didn't succeed */
+typedef enum {
+       TAP_RTP_WRONG_CODEC,
+       TAP_RTP_WRONG_LENGTH,
+       TAP_RTP_PADDING_ERROR,
+       TAP_RTP_FILE_OPEN_ERROR,
+       TAP_RTP_NO_DATA
+} error_type_t; 
+
+typedef enum {
+       FIRST_PACKET,
+               MARK_SET,
+               NORMAL_PACKET
+} packet_type;
+
+/* structure that holds the information about the forward and reversed direction */
+struct _info_direction {
+       gboolean first_packet;
+       guint16 seq_num;
+       guint32 timestamp;
+       guint32 delta_timestamp;
+       double delay;
+       double jitter;
+       double time;
+       double start_time;
+       double max_delay;
+       guint32 max_nr;
+       guint16 start_seq_nr;
+       guint16 stop_seq_nr;
+       guint32 total_nr;
+       guint32 sequence;
+       gboolean under;
+       gint cycles;
+       FILE *fp;
+       guint32 count;
+       error_type_t error_type;
+       gboolean saved;
+};
+
+/* structure that holds general information about the connection 
+* and structures for both directions */
+typedef struct _user_data_t {
+       /* tap associated data*/
+       guint32 ip_src_fwd;
+       guint16 port_src_fwd;
+       guint32 ip_dst_fwd;
+       guint16 port_dst_fwd;
+       guint32 ssrc_fwd;
+       guint32 ip_src_rev;
+       guint16 port_src_rev;
+       guint32 ip_dst_rev;
+       guint16 port_dst_rev;
+       guint32 ssrc_rev;
+
+       struct _info_direction forward;
+       struct _info_direction reversed;
+
+       char f_tempname[100];
+       char r_tempname[100];
+
+       /* dialog associated data */
+       dialog_data_t dlg;
+
+#ifdef USE_CONVERSATION_GRAPH
+       time_series_t series_fwd;
+       time_series_t series_rev;
+#endif
+} user_data_t;
+
+
+typedef const guint8 * ip_addr_p;
+
+
+/****************************************************************************/
+/* TAP FUNCTIONS */
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+static void
+rtp_reset(user_data_t *user_data _U_)
+{
+       user_data->forward.first_packet = TRUE;
+       user_data->reversed.first_packet = TRUE;
+       user_data->forward.max_delay = 0;
+       user_data->reversed.max_delay = 0;
+       user_data->forward.delay = 0;
+       user_data->reversed.delay = 0;
+       user_data->forward.jitter = 0;
+       user_data->reversed.jitter = 0;
+       user_data->forward.timestamp = 0;
+       user_data->reversed.timestamp = 0;
+       user_data->forward.max_nr = 0;
+       user_data->reversed.max_nr = 0;
+       user_data->forward.total_nr = 0;
+       user_data->reversed.total_nr = 0;
+       user_data->forward.sequence = 0;
+       user_data->reversed.sequence = 0;
+       user_data->forward.start_seq_nr = 0;
+       user_data->reversed.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
+       user_data->forward.stop_seq_nr = 0;
+       user_data->reversed.stop_seq_nr = 0;
+       user_data->forward.cycles = 0;
+       user_data->reversed.cycles = 0;
+       user_data->forward.under = FALSE;
+       user_data->reversed.under = FALSE;
+       user_data->forward.saved = FALSE;
+       user_data->reversed.saved = FALSE;
+       user_data->forward.start_time = 0;
+       user_data->reversed.start_time = 0;
+       user_data->forward.time = 0;
+       user_data->reversed.time = 0;
+       user_data->forward.count = 0;
+       user_data->reversed.count = 0;
+
+#ifdef USE_CONVERSATION_GRAPH
+       if (user_data->dlg.graph_window != NULL)
+               gtk_widget_destroy(user_data->dlg.graph_window);
+       
+       g_array_free(user_data->series_fwd.value_pairs, TRUE);
+       user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
+
+       g_array_free(user_data->series_rev.value_pairs, TRUE);
+       user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
+#endif
+
+       /* XXX check for error at fclose? */
+       if (user_data->forward.fp != NULL)
+               fclose(user_data->forward.fp); 
+       if (user_data->reversed.fp != NULL)
+               fclose(user_data->reversed.fp); 
+       user_data->forward.fp = fopen(user_data->f_tempname, "wb"); 
+       if (user_data->forward.fp == NULL)
+               user_data->forward.error_type = TAP_RTP_FILE_OPEN_ERROR;
+       user_data->reversed.fp = fopen(user_data->r_tempname, "wb");
+       if (user_data->reversed.fp == NULL)
+               user_data->reversed.error_type = TAP_RTP_FILE_OPEN_ERROR;
+       return;
+}
+
+/****************************************************************************/
+/* here we can redraw the output */
+/* not used yet */
+static void rtp_draw(void *prs _U_)
+{
+       return;
+}
+
+static int do_calculation(GtkWidget *clist, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo); 
+static void add_to_clist(GtkWidget *clist, guint32 number, guint16 seq_num,
+                         double delay, double jitter, gboolean status, gboolean marker,
+                         gchar *timeStr, guint32 pkt_len);
+
+/****************************************************************************/
+/* whenever a RTP packet is seen by the tap listener */
+/* this function works as follows:
+* 1) packets that are not displayed are ignored
+*      return
+* 3) if not, is current packet matching the forward direction
+*      if yes, call the function that does the calculation and saves the voice info
+* 4) if not, is current packet matching the reversed connection
+*      if yes, call the function that does the calculation and saves the voice info
+*/
+static int rtp_packet(user_data_t *user_data, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo)
+{
+#ifdef USE_CONVERSATION_GRAPH
+       value_pair_t vp;
+#endif
+
+       /* we ignore packets that are not displayed */
+       if (pinfo->fd->flags.passed_dfilter == 0)
+               return 0;
+
+       /* is it the forward direction?  */
+       else if (user_data->ssrc_fwd == rtpinfo->info_sync_src)  {
+#ifdef USE_CONVERSATION_GRAPH
+               vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
+               vp.fnumber = pinfo->fd->num;
+               g_array_append_val(user_data->series_fwd.value_pairs, vp);
+#endif
+
+               if (user_data->forward.first_packet != FALSE) 
+                       /* first argument is the direction TRUE == forward */
+                       return do_calculation(user_data->dlg.clist_fwd, FIRST_PACKET, &user_data->forward, rtpinfo, pinfo);
+               else if (rtpinfo->info_marker_set != FALSE)
+                       return do_calculation(user_data->dlg.clist_fwd, MARK_SET, &user_data->forward, rtpinfo, pinfo);
+               else
+                       return do_calculation(user_data->dlg.clist_fwd, NORMAL_PACKET, &user_data->forward, rtpinfo, pinfo);
+       }
+       /* is it the reversed direction? */
+       else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
+#ifdef USE_CONVERSATION_GRAPH
+               vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
+               vp.fnumber = pinfo->fd->num;
+               g_array_append_val(user_data->series_rev.value_pairs, vp);
+#endif
+
+               if (user_data->reversed.first_packet != FALSE) 
+                       return do_calculation(user_data->dlg.clist_rev, FIRST_PACKET, &user_data->reversed, rtpinfo, pinfo);
+               else if (rtpinfo->info_marker_set != FALSE)
+                       return do_calculation(user_data->dlg.clist_rev, MARK_SET, &user_data->reversed, rtpinfo, pinfo);
+               else
+                       return do_calculation(user_data->dlg.clist_rev, NORMAL_PACKET, &user_data->reversed, rtpinfo, pinfo);
+       }
+
+       return 0;
+}
+
+
+/****************************************************************************/
+static int do_calculation(GtkWidget *clist, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo)
+{
+       struct _info_direction *ptr=ptrs;
+       struct _rtp_info *pri=vpri;
+       packet_info *pinfo = vpinfo;
+       guint i;
+       double current_time;
+       double current_jitter;
+       guint8 *data;
+       gint16 tmp;
+
+       guint16 msecs;
+       gchar timeStr[32];
+
+       struct tm *tm_tmp;
+       time_t then;
+
+       then = pinfo->fd->abs_secs;
+       msecs = (guint16)(pinfo->fd->abs_usecs/1000);
+       
+       tm_tmp = localtime(&then);
+       snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
+               tm_tmp->tm_mon + 1,
+               tm_tmp->tm_mday,
+               tm_tmp->tm_year + 1900,
+               tm_tmp->tm_hour,
+               tm_tmp->tm_min,
+               tm_tmp->tm_sec,
+               msecs);
+
+       /* store the current time and calculate the current jitter */
+       current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
+       current_jitter = ptr->jitter + ( fabs (current_time - (ptr->time) -
+               ((double)(pri->info_timestamp)-(double)(ptr->timestamp))/8000)- ptr->jitter)/16;
+       ptr->delay =  current_time-(ptr->time);
+
+       /* We have 3 possibilities:
+       *  is this the first packet we got in this direction? */
+       if (pkt_type == FIRST_PACKET) {
+               ptr->first_packet = FALSE;
+               ptr->start_seq_nr = pri->info_seq_num;
+               ptr->start_time = current_time;
+               add_to_clist(clist,
+                       pinfo->fd->num, pri->info_seq_num, 0,
+                       pri->info_marker_set? TRUE: FALSE, TRUE, FALSE,
+                       timeStr, pinfo->fd->pkt_len);
+               if (ptr->fp == NULL) {
+                       ptr->saved = FALSE;
+                       ptr->error_type = TAP_RTP_FILE_OPEN_ERROR;
+               }
+               else
+                       ptr->saved = TRUE;
+       }
+       /* or is it a packet with the mark bit set? */
+       else if (pkt_type == MARK_SET) {
+               ptr->delta_timestamp = pri->info_timestamp - ptr->timestamp;
+               add_to_clist(clist,
+                       pinfo->fd->num, pri->info_seq_num, current_time - (ptr->time),
+                       current_jitter, ptr->seq_num+1 == pri->info_seq_num? TRUE: FALSE, TRUE,
+                       timeStr, pinfo->fd->pkt_len);
+       }
+       /* if neither then it is a "normal" packet pkt_type == NORMAL_PACKET */
+       else {
+               if (ptr->delay > ptr->max_delay) {
+                       ptr->max_delay = ptr->delay;
+                       ptr->max_nr = pinfo->fd->num;
+               }
+               add_to_clist(clist,
+                       pinfo->fd->num, pri->info_seq_num, current_time -(ptr->time),
+                       current_jitter , ptr->seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE,
+                       timeStr, pinfo->fd->pkt_len);
+       }
+
+       /* When calculating expected rtp packets the seq number can wrap around
+       * so we have to count the number of cycles
+       * Variable cycles counts the wraps around in forwarding connection and
+       * under is flag that indicates where we are
+       *
+       * XXX how to determine number of cycles with all possible lost, late
+       * and duplicated packets without any doubt? It seems to me, that
+       * because of all possible combination of late, duplicated or lost
+       * packets, this can only be more or less good approximation
+       *
+       * There are some combinations (rare but theoretically possible),
+       * where below code won't work correctly - statistic may be wrong then.
+       */
+
+       /* so if the current sequence number is less than the start one
+       * we assume, that there is another cycle running */
+       if ((pri->info_seq_num < ptr->start_seq_nr) && (ptr->under == FALSE)){
+               ptr->cycles++;
+               ptr->under = TRUE;
+       }
+       /* what if the start seq nr was 0? Then the above condition will never
+       * be true, so we add another condition. XXX The problem would arise
+       * if one of the packets with seq nr 0 or 65535 would be lost or late */
+       else if ((pri->info_seq_num == 0) && (ptr->stop_seq_nr == 65535) &&
+               (ptr->under == FALSE)){
+               ptr->cycles++;
+               ptr->under = TRUE;
+       }
+       /* the whole round is over, so reset the flag */
+       else if ((pri->info_seq_num > ptr->start_seq_nr) && (ptr->under != FALSE)) {
+               ptr->under = FALSE;
+       }
+
+       /* Since it is difficult to count lost, duplicate or late packets separately,
+       * we would like to know at least how many times the sequence number was not ok */
+
+       /* if the current seq number equals the last one or if we are here for
+       * the first time, then it is ok, we just store the current one as the last one */
+       if ( ( ptr->seq_num+1 == pri->info_seq_num) || (pkt_type == FIRST_PACKET) )
+               ptr->seq_num = pri->info_seq_num;
+       /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
+       else if ( (ptr->seq_num == 65535) && (pri->info_seq_num == 0) )
+               ptr->seq_num = pri->info_seq_num;
+       /* lost packets */
+       else if (ptr->seq_num+1 < pri->info_seq_num) {
+               ptr->seq_num = pri->info_seq_num;
+               ptr->sequence++;
+       }
+       /* late or duplicated */
+       else if (ptr->seq_num+1 > pri->info_seq_num)
+               ptr->sequence++;
+
+       ptr->time = current_time;
+       ptr->timestamp = pri->info_timestamp;
+       ptr->stop_seq_nr = pri->info_seq_num;
+       ptr->total_nr++;
+
+       /* save the voice information */
+       /* if there was already an error, we quit */
+       if (ptr->saved == FALSE)
+               return 0;
+
+       /* if the captured length and packet length aren't equal, we quit
+       * because there is some information missing */
+       if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
+               ptr->saved = FALSE;
+               ptr->error_type = TAP_RTP_WRONG_LENGTH;
+               return 0;
+       }
+
+       /* if padding bit is set, but the padding count is bigger
+       * then the whole RTP data - error with padding count */
+       if ( (pri->info_padding_set != FALSE) &&
+               (pri->info_padding_count > pri->info_payload_len) ) {
+               ptr->saved = FALSE;
+               ptr->error_type = TAP_RTP_PADDING_ERROR;
+               return 0;
+       }
+
+       /* do we need to insert some silence? */
+       if ((pkt_type == MARK_SET) &&
+               (ptr->delta_timestamp > (pri->info_payload_len - pri->info_padding_count)) )  {
+               /* the amount of silence should be the difference between
+               * the last timestamp and the current one minus x
+               * x should equal the amount of information in the last frame
+               * XXX not done yet */
+               for(i=0; i < (ptr->delta_timestamp - pri->info_payload_len -
+                       pri->info_padding_count); i++) {
+                       tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
+                       fwrite(&tmp, 2, 1, ptr->fp);
+                       ptr->count++;
+               }
+               fflush(ptr->fp);
+       }
+
+       /* ulaw? */
+       if (pri->info_payload_type == 0) {
+               /* we put the pointer at the beggining of the RTP data, that is
+               * at the end of the current frame minus the length of the
+               * padding count minus length of the RTP data */
+               data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len);
+               for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) {
+                       tmp = (gint16 )ulaw2linear((unsigned char)*data);
+                       fwrite(&tmp, 2, 1, ptr->fp);
+                       ptr->count++;
+               }
+               fflush(ptr->fp);
+               ptr->saved = TRUE;
+               return 0;
+       }
+
+       /* alaw? */
+       else if (pri->info_payload_type == 8) {
+               data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len);
+               for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) {
+                       tmp = (gint16 )alaw2linear((unsigned char)*data);
+                       fwrite(&tmp, 2, 1, ptr->fp);
+                       ptr->count++;
+               }
+               fflush(ptr->fp);
+               ptr->saved = TRUE;
+               return 0;
+       }
+
+       /* unsupported codec or XXX other error */
+       else {
+               ptr->saved = FALSE;
+               ptr->error_type = TAP_RTP_WRONG_CODEC;
+               return 0;
+       }
+       return 0;
+}
+
+
+/****************************************************************************/
+/* CALLBACKS */
+
+/****************************************************************************/
+/* XXX just copied from gtk/rpc_stat.c */
+void protect_thread_critical_region(void);
+void unprotect_thread_critical_region(void);
+
+
+/****************************************************************************/
+/* close the dialog window and remove the tap listener */
+static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
+{
+       protect_thread_critical_region();
+       remove_tap_listener(user_data);
+       unprotect_thread_critical_region();
+
+       if (user_data->forward.fp != NULL)
+               fclose(user_data->forward.fp);
+       if (user_data->reversed.fp != NULL)
+               fclose(user_data->reversed.fp);
+       remove(user_data->f_tempname);
+       remove(user_data->r_tempname);
+
+       /* Is there a save voice window open? */
+       if (user_data->dlg.save_voice_as_w != NULL)
+               gtk_widget_destroy(user_data->dlg.save_voice_as_w);
+
+#ifdef USE_CONVERSATION_GRAPH
+       /* Is there a graph window open? */
+       if (user_data->dlg.graph_window != NULL)
+               gtk_widget_destroy(user_data->dlg.graph_window);
+#endif
+
+       g_free(user_data);
+}
+
+
+/****************************************************************************/
+static void on_clist_select_row(GtkCList        *clist _U_,
+                                gint             row _U_,
+                                gint             column _U_,
+                                GdkEvent        *event _U_,
+                                user_data_t     *user_data _U_)
+{
+       user_data->dlg.selected_clist = clist;
+       user_data->dlg.selected_row = row;
+}
+
+
+#ifdef USE_CONVERSATION_GRAPH
+/****************************************************************************/
+/* when the graph window gets destroyed */
+static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
+{
+       /* note that graph window has been destroyed */
+       user_data->dlg.graph_window = NULL;
+}
+
+/****************************************************************************/
+static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
+{
+       guint row;
+       GtkCList *clist = NULL;
+       if (vp.fnumber != 0) {
+               clist = GTK_CLIST(user_data->dlg.clist_fwd);
+               row = gtk_clist_find_row_from_data(clist,
+                               GUINT_TO_POINTER(vp.fnumber));
+               if (row==-1) {
+                       clist = GTK_CLIST(user_data->dlg.clist_rev);
+                       row = gtk_clist_find_row_from_data(clist,
+                                       GUINT_TO_POINTER(vp.fnumber));
+               }
+               if (row!=-1) {
+                       gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
+                               (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
+                       gtk_clist_select_row(clist, row, 0);
+                       gtk_clist_moveto(clist, row, 0, 0.5, 0);
+               }
+       }
+}
+
+
+/****************************************************************************/
+static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       gchar title1[80];
+       gchar title2[80];
+       GList *list = NULL;
+       
+       if (user_data->dlg.graph_window != NULL) {
+               /* There's already a graph window; reactivate it. */
+               reactivate_window(user_data->dlg.graph_window);
+               return;
+       }
+               
+       list = g_list_append(list, &(user_data->series_fwd));
+       list = g_list_append(list, &(user_data->series_rev));
+
+       user_data->series_fwd.color.pixel = 0;
+       user_data->series_fwd.color.red = 0x80ff;
+       user_data->series_fwd.color.green = 0xe0ff;
+       user_data->series_fwd.color.blue = 0xffff;
+       user_data->series_fwd.yvalue = 0.5;
+
+       user_data->series_rev.color.pixel = 0;
+       user_data->series_rev.color.red = 0x60ff;
+       user_data->series_rev.color.green = 0xc0ff;
+       user_data->series_rev.color.blue = 0xffff;
+       user_data->series_rev.yvalue = -0.5;
+
+       g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
+               ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
+               user_data->port_src_fwd,
+               ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
+               user_data->port_dst_fwd,
+               user_data->ssrc_fwd);
+
+       g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
+               ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
+               user_data->port_src_rev,
+               ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
+               user_data->port_dst_rev,
+               user_data->ssrc_rev);
+
+       user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
+               &graph_selection_callback, user_data);
+       gtk_signal_connect(GTK_OBJECT(user_data->dlg.graph_window), "destroy",
+               GTK_SIGNAL_FUNC(on_destroy_graph), user_data);
+}
+#endif /*USE_CONVERSATION_GRAPH*/
+
+
+/****************************************************************************/
+static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       guint fnumber;
+
+       if (user_data->dlg.selected_clist!=NULL) {
+               fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
+                       GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
+               goto_frame(&cfile, fnumber);
+       }
+}
+
+
+static void draw_stat(user_data_t *user_data);
+
+/****************************************************************************/
+/* re-dissects all packets */
+static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
+       gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
+       redissect_packets(&cfile);
+       draw_stat(user_data);
+}
+
+/****************************************************************************/
+/* on_destroy is automatically called after that */
+static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
+       gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
+}
+
+
+/****************************************************************************/
+/* when we want to save the information */
+static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
+{
+       gchar *g_dest;
+       GtkWidget *rev, *forw, *both;
+       user_data_t *user_data;
+       
+       FILE *fp;
+       char *columnText;
+       int i,j;
+       
+       g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
+       
+       /* Perhaps the user specified a directory instead of a file.
+       Check whether they did. */
+       if (test_for_directory(g_dest) == EISDIR) {
+               /* It's a directory - set the file selection box to display it. */
+               set_last_open_dir(g_dest);
+               g_free(g_dest);
+               gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
+               return;
+       }
+       
+       rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
+       forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
+       both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
+       user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
+       
+       if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
+               fp = fopen(g_dest, "w");
+               
+               if (GTK_TOGGLE_BUTTON(both)->active) {
+                       fprintf(fp, "Forward\n");
+               }
+               
+               for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
+                       if (j == 0) {
+                               fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
+                       } else {
+                               fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_fwd)->column[j].title);
+                       }
+               }
+               fprintf(fp,"\n");
+               for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
+                       for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
+                               gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
+                               if (j == 0) {
+                                       fprintf(fp,"%s",columnText);
+                               } else {
+                                       fprintf(fp,",%s",columnText);
+                               }
+                       }
+                       fprintf(fp,"\n");
+               }
+               
+               fclose(fp);
+       }
+       
+       if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
+               
+               if (GTK_TOGGLE_BUTTON(both)->active) {
+                       fp = fopen(g_dest, "a");
+                       fprintf(fp, "\nReverse\n");
+               } else {
+                       fp = fopen(g_dest, "w");
+               }
+               for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
+                       if (j == 0) {
+                               fprintf(fp,"%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
+                       } else {
+                               fprintf(fp,",%s",GTK_CLIST(user_data->dlg.clist_rev)->column[j].title);
+                       }
+               }
+               fprintf(fp,"\n");
+               for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
+                       for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
+                               gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
+                               if (j == 0) {
+                                       fprintf(fp,"%s",columnText);
+                               } else {
+                                       fprintf(fp,",%s",columnText);
+                               }
+                       }
+                       fprintf(fp,"\n");
+               }
+               fclose(fp);
+       }
+
+       gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
+}
+
+static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
+{
+       user_data->dlg.save_csv_as_w = NULL;
+}
+
+/* when the user wants to save the csv information in a file */
+static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       GtkWidget *vertb;
+       GtkWidget *table1;
+       GtkWidget *label_format;
+       GtkWidget *channels_label;
+       GSList *channels_group = NULL;
+       GtkWidget *forward_rb;
+       GtkWidget *reversed_rb;
+       GtkWidget *both_rb;
+       GtkWidget *ok_bt;
+       
+       if (user_data->dlg.save_csv_as_w != NULL) {
+               /* There's already a Save CSV info dialog box; reactivate it. */
+               reactivate_window(user_data->dlg.save_csv_as_w);
+               return;
+       }
+       
+       user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
+       gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_csv_as_w), "destroy",
+               GTK_SIGNAL_FUNC(save_csv_as_destroy_cb), user_data);
+       
+       /* Container for each row of widgets */
+       vertb = gtk_vbox_new(FALSE, 0);
+       gtk_container_border_width(GTK_CONTAINER(vertb), 5);
+       gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
+               vertb, FALSE, FALSE, 0);
+       gtk_widget_show (vertb);
+       
+       table1 = gtk_table_new (2, 4, FALSE);
+       gtk_widget_show (table1);
+       gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+       gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+       gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+       
+       label_format = gtk_label_new ("Format: Comma Separated Values");
+       gtk_widget_show (label_format);
+       gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       
+       channels_label = gtk_label_new ("Channels:");
+       gtk_widget_show (channels_label);
+       gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
+       
+       forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
+       gtk_widget_show (forward_rb);
+       gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
+       gtk_widget_show (reversed_rb);
+       gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       both_rb = gtk_radio_button_new_with_label (channels_group, "both");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
+       gtk_widget_show (both_rb);
+       gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
+       
+       ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
+       OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
+       OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
+       OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
+       OBJECT_SET_DATA(ok_bt, "user_data", user_data);
+       
+       /* Connect the cancel_button to destroy the widget */
+       SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
+               "clicked", (GtkSignalFunc)gtk_widget_destroy,
+               user_data->dlg.save_csv_as_w);
+       
+       /* Catch the "key_press_event" signal in the window, so that we can catch
+       the ESC key being pressed and act as if the "Cancel" button had
+       been selected. */
+       dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
+       
+       gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
+               GTK_SIGNAL_FUNC(save_csv_as_ok_cb), user_data->dlg.save_csv_as_w);
+       
+       gtk_widget_show(user_data->dlg.save_csv_as_w);
+}
+
+
+/****************************************************************************/
+static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
+{
+       /* Note that we no longer have a Save voice info dialog box. */
+       user_data->dlg.save_voice_as_w = NULL;
+}
+
+/****************************************************************************/
+/* here we save it into a file that user specified */
+/* XXX what about endians here? could go something wrong? */
+static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
+{
+       int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
+       gint16 f_pd;
+       gint16 r_pd;
+       gchar pd[1];
+       guint32 f_write_silence = 0;
+       guint32 r_write_silence = 0;
+       progdlg_t *progbar;
+       guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
+       gboolean stop_flag = FALSE;
+
+       forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
+       if (forw_fd < 0) 
+               return FALSE;
+       rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
+       if (rev_fd < 0) {
+               close(forw_fd); 
+               return FALSE;
+       }
+
+       /* open file for saving */
+       to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+       if (to_fd < 0) {
+               close(forw_fd);
+               close(rev_fd);
+               return FALSE;
+       }
+
+       progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag);
+
+       /* First we write the .au header. XXX Hope this is endian independant */
+       /* the magic word 0x2e736e64 == .snd */
+       *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
+       *pd = (unsigned char)0x73; write(to_fd, pd, 1);
+       *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
+       *pd = (unsigned char)0x64; write(to_fd, pd, 1);
+       /* header offset == 24 bytes */
+       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       *pd = (unsigned char)0x18; write(to_fd, pd, 1);
+       /* total length, it is permited to set this to 0xffffffff */
+       *pd = (unsigned char)0xff; write(to_fd, pd, 1); 
+       write(to_fd, pd, 1); 
+       write(to_fd, pd, 1); 
+       write(to_fd, pd, 1);
+       /* encoding format == 8 bit ulaw */
+       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       *pd = (unsigned char)0x01; write(to_fd, pd, 1);
+       /* sample rate == 8000 Hz */
+       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
+       *pd = (unsigned char)0x40; write(to_fd, pd, 1);
+       /* channels == 1 */
+       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       write(to_fd, pd, 1);
+       *pd = (unsigned char)0x01; write(to_fd, pd, 1);
+       
+       switch (channels) {
+               /* only forward direction */
+               case 1: {
+                       progbar_count = user_data->forward.count;
+                       progbar_quantum = user_data->forward.count/100;
+                       while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
+                               if(stop_flag) 
+                                       break;
+                               if((count > progbar_nextstep) && (count <= progbar_count)) {
+                                       update_progress_dlg(progbar, 
+                                               (gfloat) count/progbar_count, "Saving");
+                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
+                               }
+                               count++;
+                               *pd = (unsigned char)linear2ulaw(f_pd);
+                               fwritten = write(to_fd, pd, 1);
+                               if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
+                                       close(forw_fd);
+                                       close(rev_fd);
+                                       close(to_fd);
+                                       destroy_progress_dlg(progbar);
+                                       return FALSE;
+                               }
+                       }
+                       break;
+               }
+               /* only reversed direction */
+               case 2: {
+                       progbar_count = user_data->reversed.count;
+                       progbar_quantum = user_data->reversed.count/100;
+                       while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
+                               if(stop_flag) 
+                                       break;
+                               if((count > progbar_nextstep) && (count <= progbar_count)) {
+                                       update_progress_dlg(progbar, 
+                                               (gfloat) count/progbar_count, "Saving");
+                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
+                               }
+                               count++;
+                               *pd = (unsigned char)linear2ulaw(r_pd);
+                               rwritten = write(to_fd, pd, 1);
+                               if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
+                                       close(forw_fd);
+                                       close(rev_fd);
+                                       close(to_fd);
+                                       destroy_progress_dlg(progbar);
+                                       return FALSE;
+                               }
+                       }
+                       break;
+               }
+               /* both directions */
+               default: {
+                       (user_data->forward.count > user_data->reversed.count) ? 
+                                       (progbar_count = user_data->forward.count) : 
+                                               (progbar_count = user_data->reversed.count);
+                       progbar_quantum = progbar_count/100;
+                       /* since conversation in one way can start later than in the other one, 
+                        * we have to write some silence information for one channel */
+                       if (user_data->forward.start_time > user_data->reversed.start_time) {
+                               f_write_silence = 
+                                       (user_data->forward.start_time-user_data->reversed.start_time)*8000;
+                       }
+                       else if (user_data->forward.start_time < user_data->reversed.start_time) {
+                               r_write_silence = 
+                                       (user_data->reversed.start_time-user_data->forward.start_time)*8000;
+                       }
+                       for(;;) {
+                               if(stop_flag) 
+                                       break;
+                               if((count > progbar_nextstep) && (count <= progbar_count)) {
+                                       update_progress_dlg(progbar, 
+                                               (gfloat) count/progbar_count, "Saving");
+                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
+                               }
+                               count++;
+                               if(f_write_silence > 0) {
+                                       rread = read(rev_fd, &r_pd, 2);
+                                       f_pd = 0;
+                                       fread = 1;
+                                       f_write_silence--;
+                               }
+                               else if(r_write_silence > 0) {
+                                       fread = read(forw_fd, &f_pd, 2);
+                                       r_pd = 0;
+                                       rread = 1;
+                                       r_write_silence--;
+                               }
+                               else {
+                                       fread = read(forw_fd, &f_pd, 2); 
+                                       rread = read(rev_fd, &r_pd, 2);
+                               }
+                               if ((rread == 0) && (fread == 0)) 
+                                       break;
+                               *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
+                               rwritten = write(to_fd, pd, 1);
+                               if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
+                                       close(forw_fd);
+                                       close(rev_fd);
+                                       close(to_fd);
+                                       destroy_progress_dlg(progbar);
+                                       return FALSE;
+                               }
+                       }
+               }
+       }
+       destroy_progress_dlg(progbar);
+       close(forw_fd);
+       close(rev_fd);
+       close(to_fd);
+       return TRUE;
+}
+
+
+/****************************************************************************/
+/* the user wants to save in a file */
+/* XXX support for different formats is currently commented out */
+static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
+{
+       gchar *g_dest;
+       /*GtkWidget *wav, *au, *sw;*/
+       GtkWidget *rev, *forw, *both;
+       user_data_t *user_data;
+       gint channels /*, format*/;
+       
+       g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
+       
+       /* Perhaps the user specified a directory instead of a file.
+       Check whether they did. */
+       if (test_for_directory(g_dest) == EISDIR) {
+               /* It's a directory - set the file selection box to display it. */
+               set_last_open_dir(g_dest);
+               g_free(g_dest);
+               gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
+               return;
+       }
+       
+       /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
+       au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
+       sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
+       rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
+       forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
+       both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
+       user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
+       
+       /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
+       * we don't support that codec. So we pop up a warning. Maybe it would be better to
+       * disable the ok button or disable the buttons for direction if only one is not ok. The
+       * problem is if we open the save voice dialog and then click the refresh button and maybe 
+       * the state changes, so we can't save anymore. In this case we should be able to update
+       * the buttons. For now it is easier if we put the warning when the ok button is pressed.
+       */
+       
+       /* we can not save in both dirctions */
+       if ((user_data->forward.saved == FALSE) && (user_data->reversed.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
+               /* there are many combinations here, we just exit when first matches */
+               if ((user_data->forward.error_type == TAP_RTP_WRONG_CODEC) || 
+                       (user_data->reversed.error_type == TAP_RTP_WRONG_CODEC))
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save in a file: Unsupported codec!");
+               else if ((user_data->forward.error_type == TAP_RTP_WRONG_LENGTH) || 
+                       (user_data->reversed.error_type == TAP_RTP_WRONG_LENGTH))
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save in a file: Wrong length of captured packets!");
+               else if ((user_data->forward.error_type == TAP_RTP_PADDING_ERROR) || 
+                       (user_data->reversed.error_type == TAP_RTP_PADDING_ERROR))
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save in a file: RTP data with padding!");
+               else  
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save in a file: File I/O problem!");
+               return;
+       }
+       /* we can not save forward direction */
+       else if ((user_data->forward.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
+               (GTK_TOGGLE_BUTTON (both)->active))) {  
+               if (user_data->forward.error_type == TAP_RTP_WRONG_CODEC)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save forward direction in a file: Unsupported codec!");
+               else if (user_data->forward.error_type == TAP_RTP_WRONG_LENGTH)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save forward direction in a file: Wrong length of captured packets!");
+               else if (user_data->forward.error_type == TAP_RTP_PADDING_ERROR)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save forward direction in a file: RTP data with padding!");
+               else
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, 
+                       "Can't save forward direction in a file: File I/O problem!");
+               return;
+       }
+       /* we can not save reversed direction */
+       else if ((user_data->reversed.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
+               (GTK_TOGGLE_BUTTON (both)->active))) {  
+               if (user_data->reversed.error_type == TAP_RTP_WRONG_CODEC)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save reversed direction in a file: Unsupported codec!");
+               else if (user_data->reversed.error_type == TAP_RTP_WRONG_LENGTH)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save reversed direction in a file: Wrong length of captured packets!");
+               else if (user_data->reversed.error_type == TAP_RTP_PADDING_ERROR)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save reversed direction in a file: RTP data with padding!");
+               else if (user_data->reversed.error_type == TAP_RTP_NO_DATA)
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save reversed direction in a file: No RTP data!");
+               else
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "Can't save reversed direction in a file: File I/O problem!");
+               return;
+       }
+       
+       /*if (GTK_TOGGLE_BUTTON (wav)->active)
+       format = 1;
+       else if (GTK_TOGGLE_BUTTON (au)->active)
+       format = 2;
+       else if (GTK_TOGGLE_BUTTON (sw)->active)
+       format = 3;*/
+       
+       if (GTK_TOGGLE_BUTTON (rev)->active)
+               channels = 2;
+       else if (GTK_TOGGLE_BUTTON (both)->active)
+               channels = 3;
+       else 
+               channels = 1;
+       
+       if(!copy_file(g_dest, channels/*, format*/, user_data)) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       "An error occured while saving voice in a file!");
+               return;
+       }
+       
+       gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
+}
+
+/****************************************************************************/
+/* when the user wants to save the voice information in a file */
+/* XXX support for different formats is currently commented out */
+static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+       GtkWidget *vertb;
+       GtkWidget *table1;
+       GtkWidget *label_format;
+       GtkWidget *channels_label;
+       /*GSList *format_group = NULL;*/
+       GSList *channels_group = NULL;
+       GtkWidget *forward_rb;
+       GtkWidget *reversed_rb;
+       GtkWidget *both_rb;
+       /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
+       GtkWidget *ok_bt;
+       
+       /* if we can't save in a file: wrong codec, cut packets or other errors */
+       /* shold the error arise here or later when you click ok button ? 
+       * if we do it here, then we must disable the refresh button, so we don't do it here */
+       
+       if (user_data->dlg.save_voice_as_w != NULL) {
+               /* There's already a Save voice info dialog box; reactivate it. */
+               reactivate_window(user_data->dlg.save_voice_as_w);
+               return;
+       }
+       
+       user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
+       gtk_signal_connect(GTK_OBJECT(user_data->dlg.save_voice_as_w), "destroy",
+               GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), user_data);
+       
+       /* Container for each row of widgets */
+       vertb = gtk_vbox_new(FALSE, 0);
+       gtk_container_border_width(GTK_CONTAINER(vertb), 5);
+       gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
+               vertb, FALSE, FALSE, 0);
+       gtk_widget_show (vertb);
+       
+       table1 = gtk_table_new (2, 4, FALSE);
+       gtk_widget_show (table1);
+       gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+       gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+       gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+       
+       label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
+       gtk_widget_show (label_format);
+       gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       /* we support .au - ulaw*/ 
+       /*      wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
+       format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
+       gtk_widget_show (wav_rb);
+       gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
+       (GtkAttachOptions) (GTK_FILL),
+       (GtkAttachOptions) (0), 0, 0);
+       
+         sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit  ");
+         format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
+         gtk_widget_show (sw_rb);
+         gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
+         (GtkAttachOptions) (GTK_FILL),
+         (GtkAttachOptions) (0), 0, 0);
+         au_rb = gtk_radio_button_new_with_label (format_group, ".au");
+         format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
+         gtk_widget_show (au_rb);
+         gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
+         (GtkAttachOptions) (GTK_FILL),
+         (GtkAttachOptions) (0), 0, 0);
+       */ 
+       
+       channels_label = gtk_label_new ("Channels:");
+       gtk_widget_show (channels_label);
+       gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
+       
+       forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
+       gtk_widget_show (forward_rb);
+       gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
+       gtk_widget_show (reversed_rb);
+       gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       both_rb = gtk_radio_button_new_with_label (channels_group, "both");
+       channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
+       gtk_widget_show (both_rb);
+       gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+               (GtkAttachOptions) (GTK_FILL),
+               (GtkAttachOptions) (0), 0, 0);
+       
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
+       
+       /* if one direction is nok we don't allow saving 
+       XXX this is not ok since the user can click the refresh button and cause changes
+       but we can not update this window. So we move all the decision on the time the ok
+       button is clicked
+       if (user_data->forward.saved == FALSE) {
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
+       gtk_widget_set_sensitive(forward_rb, FALSE);
+       gtk_widget_set_sensitive(both_rb, FALSE);
+       }
+       else if (user_data->reversed.saved == FALSE) {
+       gtk_widget_set_sensitive(reversed_rb, FALSE);
+       gtk_widget_set_sensitive(both_rb, FALSE);
+       }
+       */
+       
+       ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
+       /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
+       OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
+       OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
+       OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
+       OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
+       OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
+       OBJECT_SET_DATA(ok_bt, "user_data", user_data);
+       
+       /* Connect the cancel_button to destroy the widget */
+       SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
+               "clicked", (GtkSignalFunc)gtk_widget_destroy,
+               user_data->dlg.save_voice_as_w);
+       
+               /* Catch the "key_press_event" signal in the window, so that we can catch
+               the ESC key being pressed and act as if the "Cancel" button had
+       been selected. */
+       dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
+       
+       gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
+               GTK_SIGNAL_FUNC(save_voice_as_ok_cb), user_data->dlg.save_voice_as_w);
+       
+       gtk_widget_show(user_data->dlg.save_voice_as_w);
+}
+
+
+/****************************************************************************/
+/* when we are finished with redisection, we add the label for the statistic */
+static void draw_stat(user_data_t *user_data)
+{
+       gchar label_max[200];
+       guint32 f_expected = (user_data->forward.stop_seq_nr + user_data->forward.cycles*65536)
+               - user_data->forward.start_seq_nr + 1;
+       guint32 r_expected = (user_data->reversed.stop_seq_nr + user_data->reversed.cycles*65536)
+               - user_data->reversed.start_seq_nr + 1;
+       gint32 f_lost = f_expected - user_data->forward.total_nr;
+       gint32 r_lost = r_expected - user_data->reversed.total_nr;
+
+       g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
+               "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"
+               "   Sequence errors = %u",
+               user_data->forward.max_delay, user_data->forward.max_nr, user_data->forward.total_nr,
+               f_expected, f_lost, user_data->forward.sequence);
+
+       gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
+
+       g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n\n"
+               "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"
+               "   Sequence errors = %u",
+               user_data->reversed.max_delay, user_data->reversed.max_nr, user_data->reversed.total_nr,
+               r_expected, r_lost, user_data->reversed.sequence);
+
+       gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
+
+       return ;
+}
+
+/****************************************************************************/
+/* append a line to clist */
+static void add_to_clist(GtkWidget *clist, guint32 number, guint16 seq_num,
+                         double delay, double jitter, gboolean status, gboolean marker,
+                         gchar *timeStr, guint32 pkt_len)
+{
+       guint added_row;
+       gchar *data[8];
+       gchar field[8][32];
+
+       data[0]=&field[0][0];
+       data[1]=&field[1][0];
+       data[2]=&field[2][0];
+       data[3]=&field[3][0];
+       data[4]=&field[4][0];
+       data[5]=&field[5][0];
+       data[6]=&field[6][0];
+       data[7]=&field[7][0];
+
+       g_snprintf(field[0], 20, "%u", number);
+       g_snprintf(field[1], 20, "%u", seq_num);
+       g_snprintf(field[2], 20, "%f", delay);
+       g_snprintf(field[3], 20, "%f", jitter);
+       g_snprintf(field[4], 20, "%s", marker? "SET" : "");
+       g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr.");
+       g_snprintf(field[6], 32, "%s", timeStr);
+       g_snprintf(field[7], 20, "%u", pkt_len);
+
+       added_row = gtk_clist_append(GTK_CLIST(clist), data);
+       gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
+}
+
+/****************************************************************************/
+/* Create the dialog box with all widgets */
+void create_rtp_dialog(user_data_t* user_data)
+{
+       GtkWidget *window = NULL;
+       GtkWidget *clist_fwd;
+       GtkWidget *clist_rev;
+       GtkWidget *label_stats_fwd;
+       GtkWidget *label_stats_rev;
+       GtkWidget *notebook;
+
+       GtkWidget *main_vb, *page, *page_r, *label, *label1, *label2, *label3;
+       GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
+       GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt;
+#ifdef USE_CONVERSATION_GRAPH
+       GtkWidget *graph_bt;
+#endif
+
+       gchar *titles[8] =  {"Packet", "Sequence",  "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"};
+       gchar label_forward[150];
+       gchar label_reverse[150];
+
+       gchar str_ip_src[16];
+       gchar str_ip_dst[16];
+       
+
+       window = dlg_window_new("Ethereal: RTP Stream Analysis");
+       gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+       gtk_signal_connect(GTK_OBJECT(window), "destroy",
+               GTK_SIGNAL_FUNC(on_destroy), user_data);
+
+       /* Container for each row of widgets */
+       main_vb = gtk_vbox_new(FALSE, 3);
+       gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+       gtk_container_add(GTK_CONTAINER(window), main_vb);
+       gtk_widget_show(main_vb);
+
+
+       /* Notebooks... */
+       strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
+       strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
+
+       g_snprintf(label_forward, 149, 
+               "Analysing connection from  %s port %u  to  %s port %u   SSRC = %u\n", 
+               str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
+
+       strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
+       strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
+
+       g_snprintf(label_reverse, 149,
+               "Analysing connection from  %s port %u  to  %s port %u   SSRC = %u\n", 
+               str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
+
+       /* Start a notebook for flipping between sets of changes */
+       notebook = gtk_notebook_new();
+       gtk_container_add(GTK_CONTAINER(main_vb), notebook);
+       gtk_object_set_data(GTK_OBJECT(window), "notebook", notebook);
+
+       /* page for forward connection */
+       page = gtk_vbox_new(FALSE, 5);
+       gtk_container_set_border_width(GTK_CONTAINER(page), 20);
+
+       /* scrolled window */
+       scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+       gtk_widget_set_usize(scrolled_window, 600, 200);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), 
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+       /* direction label */
+       label1 = gtk_label_new(label_forward);
+       gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
+
+       /* place for some statistics */
+       label_stats_fwd = gtk_label_new("\n\n");
+       gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 5);
+
+       /* clist for the information */
+       clist_fwd = gtk_clist_new_with_titles(8, titles);
+       gtk_widget_show(clist_fwd);
+       gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
+       gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+       gtk_signal_connect(GTK_OBJECT (clist_fwd), "select_row",
+                                         GTK_SIGNAL_FUNC (on_clist_select_row),
+                                         user_data);
+       /* Hide date and length column */
+       gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
+       gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
+
+       /* label */
+       label = gtk_label_new("     Forward Direction     ");
+       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+
+       /* column width and justification */
+       gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 40);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
+
+       /* same page for reversed connection */
+       page_r = gtk_vbox_new(FALSE, 5);
+       gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
+       scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
+       gtk_widget_set_usize(scrolled_window_r, 600, 200);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r), 
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+       label3 = gtk_label_new(label_reverse);
+       gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
+       label_stats_rev = gtk_label_new("\n\n");
+       gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 5);
+       clist_rev = gtk_clist_new_with_titles(8, titles);
+       gtk_widget_show(clist_rev);
+       gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 6, FALSE);
+       gtk_clist_set_column_visibility(GTK_CLIST(clist_rev), 7, FALSE);
+
+       gtk_signal_connect(GTK_OBJECT (clist_rev), "select_row",
+                                 GTK_SIGNAL_FUNC (on_clist_select_row),
+                                 user_data);
+
+       gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
+       gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
+       label2 = gtk_label_new("     Reversed Direction     ");
+       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
+
+       gtk_clist_set_column_width(GTK_CLIST(clist_rev), 0, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_rev), 1, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_rev), 2, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_rev), 3, 80);
+       gtk_clist_set_column_width(GTK_CLIST(clist_rev), 4, 40);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 0, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 1, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 2, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 3, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 4, GTK_JUSTIFY_CENTER);
+       gtk_clist_set_column_justification(GTK_CLIST(clist_rev), 5, GTK_JUSTIFY_CENTER);
+
+       /* page for help&about or future
+       page_help = gtk_hbox_new(FALSE, 5);
+       label4 = gtk_label_new("     Future    ");
+       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
+       frame = gtk_frame_new("");
+       text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
+       gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
+       gtk_container_add(GTK_CONTAINER(frame), text);
+       gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
+       gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
+       */
+
+       /* show all notebooks */
+       gtk_widget_show_all(notebook);
+
+       /* buttons */
+       box4 = gtk_hbutton_box_new();
+       gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
+       gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
+       gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
+       gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
+       gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
+       gtk_widget_show(box4);
+
+       voice_bt = gtk_button_new_with_label("Save payload...");
+       gtk_container_add(GTK_CONTAINER(box4), voice_bt);
+       gtk_widget_show(voice_bt);
+       gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked",
+               GTK_SIGNAL_FUNC(on_save_bt_clicked), user_data);
+
+       csv_bt = gtk_button_new_with_label("Save as CSV...");
+       gtk_container_add(GTK_CONTAINER(box4), csv_bt);
+       gtk_widget_show(csv_bt);
+       gtk_signal_connect(GTK_OBJECT(csv_bt), "clicked",
+               GTK_SIGNAL_FUNC(save_csv_as_cb), user_data);
+
+       refresh_bt = gtk_button_new_with_label("Refresh");
+       gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
+       gtk_widget_show(refresh_bt);
+       gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked",
+               GTK_SIGNAL_FUNC(on_refresh_bt_clicked), user_data);
+
+       goto_bt = gtk_button_new_with_label("Go to frame");
+       gtk_container_add(GTK_CONTAINER(box4), goto_bt);
+       gtk_widget_show(goto_bt);
+       gtk_signal_connect(GTK_OBJECT(goto_bt), "clicked",
+               GTK_SIGNAL_FUNC(on_goto_bt_clicked), user_data);
+
+#ifdef USE_CONVERSATION_GRAPH
+       graph_bt = gtk_button_new_with_label("Graph");
+       gtk_container_add(GTK_CONTAINER(box4), graph_bt);
+       gtk_widget_show(graph_bt);
+       gtk_signal_connect(GTK_OBJECT(graph_bt), "clicked",
+               GTK_SIGNAL_FUNC(on_graph_bt_clicked), user_data);
+#endif
+
+       close_bt = gtk_button_new_with_label("Close");
+       gtk_container_add(GTK_CONTAINER(box4), close_bt);
+       gtk_widget_show(close_bt);
+       gtk_signal_connect(GTK_OBJECT(close_bt), "clicked",
+               GTK_SIGNAL_FUNC(on_close_bt_clicked), user_data);
+
+       gtk_widget_show(window);
+
+       user_data->dlg.window = window;
+       user_data->dlg.clist_fwd = clist_fwd;
+       user_data->dlg.clist_rev = clist_rev;
+       user_data->dlg.label_stats_fwd = label_stats_fwd;
+       user_data->dlg.label_stats_rev = label_stats_rev;
+       user_data->dlg.notebook = notebook;
+       user_data->dlg.selected_clist = NULL;
+       user_data->dlg.selected_row = -1;
+}
+
+
+/****************************************************************************/
+static gboolean process_node(proto_item *ptree_node, header_field_info *hfinformation,
+                                                       const gchar* proto_field, guint32* p_result)
+{
+       field_info            *finfo;
+       proto_item            *proto_sibling_node;
+       header_field_info     *hfssrc;
+       ipv4_addr             *ipv4;
+
+       finfo = PITEM_FINFO(ptree_node);
+
+       if (hfinformation==(finfo->hfinfo)) {
+               hfssrc = proto_registrar_get_byname((gchar*) proto_field);
+               if (hfssrc == NULL)
+                       return FALSE;
+               for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL; 
+                                       ptree_node=g_node_next_sibling(ptree_node)) {
+                       finfo=PITEM_FINFO(ptree_node);
+                       if (hfssrc==finfo->hfinfo) {
+                               if (hfinformation->type==FT_IPv4) {
+                                       ipv4 = fvalue_get(finfo->value);
+                                       *p_result = ipv4_get_net_order_addr(ipv4);
+                               }
+                               else {
+                                       *p_result = fvalue_get_integer(finfo->value);
+                               }
+                               return TRUE;
+                       }
+               }
+       }
+
+       proto_sibling_node = g_node_next_sibling(ptree_node);
+
+       if (proto_sibling_node) {
+               return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
+       }
+       else
+       return FALSE;
+}
+
+/****************************************************************************/
+static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
+                                                                                        const gchar* proto_name,
+                                                                                        const gchar* proto_field,
+                                                                                        guint32* p_result)
+{
+       proto_item      *ptree_node;
+       header_field_info     *hfinformation;
+
+       hfinformation = proto_registrar_get_byname((gchar*) proto_name);
+       if (hfinformation == NULL)
+               return FALSE;
+
+       ptree_node = g_node_first_child(protocol_tree);
+       if (!ptree_node)
+               return FALSE;
+
+       return process_node(ptree_node, hfinformation, proto_field, p_result);
+}
+
+
+/****************************************************************************/
+/* XXX only handles RTP over IPv4, should add IPv6 support */
+void rtp_analysis(
+               guint32 ip_src_fwd,
+               guint16 port_src_fwd,
+               guint32 ip_dst_fwd,
+               guint16 port_dst_fwd,
+               guint32 ssrc_fwd,
+               guint32 ip_src_rev,
+               guint16 port_src_rev,
+               guint32 ip_dst_rev,
+               guint16 port_dst_rev,
+               guint32 ssrc_rev
+               )
+{
+       user_data_t *user_data;
+       gchar filter_text[256];
+       dfilter_t *sfcode;
+       GString *error_string;
+
+       user_data = g_malloc(sizeof(user_data_t));
+
+       user_data->ip_src_fwd = ip_src_fwd;
+       user_data->port_src_fwd = port_src_fwd;
+       user_data->ip_dst_fwd = ip_dst_fwd;
+       user_data->port_dst_fwd = port_dst_fwd;
+       user_data->ssrc_fwd = ssrc_fwd;
+       user_data->ip_src_rev = ip_src_rev;
+       user_data->port_src_rev = port_src_rev;
+       user_data->ip_dst_rev = ip_dst_rev;
+       user_data->port_dst_rev = port_dst_rev;
+       user_data->ssrc_rev = ssrc_rev;
+
+       create_rtp_dialog(user_data);
+
+       /* Try to compile the filter. */
+       strcpy(filter_text,"rtp && ip");
+       if (!dfilter_compile(filter_text, &sfcode)) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
+               return;
+       }
+
+       sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ) || ( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ))",
+               ip_to_str((ip_addr_p)&ip_src_fwd),
+               port_src_fwd,
+               ip_to_str((ip_addr_p)&ip_dst_fwd),
+               port_dst_fwd,
+               ip_to_str((ip_addr_p)&ip_src_rev),
+               port_src_rev,
+               ip_to_str((ip_addr_p)&ip_dst_rev),
+               port_dst_rev
+               );
+
+       error_string = register_tap_listener("rtp", user_data, filter_text,
+               (void*)rtp_reset, (void*)rtp_packet, (void*)rtp_draw);
+       if (error_string != NULL) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
+                       g_string_free(error_string, TRUE);
+               g_free(user_data);
+               return;
+               /*exit(1);*/
+       }
+
+       /* file names for storing sound data */
+       tmpnam(user_data->f_tempname);
+       tmpnam(user_data->r_tempname);
+       user_data->forward.fp = NULL;
+       user_data->reversed.fp = NULL;
+       user_data->dlg.save_voice_as_w = NULL;
+       user_data->dlg.save_csv_as_w = NULL;
+#ifdef USE_CONVERSATION_GRAPH
+       user_data->dlg.graph_window = NULL;
+       user_data->series_fwd.value_pairs = NULL;
+       user_data->series_rev.value_pairs = NULL;
+#endif
+
+       redissect_packets(&cfile);
+
+       draw_stat(user_data);
+}
+
+/****************************************************************************/
+/* entry point from main menu */
+void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_) 
+{
+       guint32 ip_src_fwd;
+       guint16 port_src_fwd;
+       guint32 ip_dst_fwd;
+       guint16 port_dst_fwd;
+       guint32 ssrc_fwd = 0;
+       guint32 ip_src_rev;
+       guint16 port_src_rev;
+       guint32 ip_dst_rev;
+       guint16 port_dst_rev;
+       guint32 ssrc_rev = 0;
+
+       gchar filter_text[256];
+       dfilter_t *sfcode;
+       capture_file *cf;
+       epan_dissect_t *edt;
+       gint err;
+       gboolean frame_matched;
+       frame_data *fdata;
+       GList *strinfo_list;
+       GList *filtered_list = NULL;
+       rtp_stream_info_t *strinfo;
+       guint nfound;
+
+       /* Try to compile the filter. */
+       strcpy(filter_text,"rtp && ip");
+       if (!dfilter_compile(filter_text, &sfcode)) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, dfilter_error_msg);
+               return;
+       }
+       /* we load the current file into cf variable */
+       cf = &cfile;
+       fdata = cf->current_frame;
+       
+       /* we are on the selected frame now */
+       if (fdata == NULL)
+               return; /* if we exit here it's an error */
+
+       /* dissect the current frame */
+       if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL,
+                       file_read_error_message(err), cf->filename);
+               return;
+       }
+       edt = epan_dissect_new(TRUE, FALSE);
+       epan_dissect_prime_dfilter(edt, sfcode);
+       epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
+       frame_matched = dfilter_apply_edt(sfcode, edt);
+       
+       /* if it is not an rtp frame, show the rtpstream dialog */
+       frame_matched = dfilter_apply_edt(sfcode, edt);
+       if (frame_matched != 1) {
+               rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
+               return;
+/*
+               epan_dissect_free(edt);
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "You didn't choose a RTP packet!");
+               return;
+*/
+       }
+
+       /* ok, it is a RTP frame, so let's get the ip and port values */
+       g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
+       g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
+       port_src_fwd = edt->pi.srcport;
+       port_dst_fwd = edt->pi.destport;
+
+       /* assume the inverse ip/port combination for the reverse direction */
+       g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
+       g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
+       port_src_rev = edt->pi.destport;
+       port_dst_rev = edt->pi.srcport;
+       
+       /* now we need the SSRC value of the current frame */
+       if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
+               simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, "SSRC value couldn't be found!");
+               return;
+       }
+
+       /* search for reversed direction in the global rtp streams list */
+       nfound = 0;
+       strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
+       while (strinfo_list)
+       {
+               strinfo = (rtp_stream_info_t*)(strinfo_list->data);
+               if (strinfo->src_addr==ip_src_fwd
+                       && strinfo->src_port==port_src_fwd
+                       && strinfo->dest_addr==ip_dst_fwd
+                       && strinfo->dest_port==port_dst_fwd)
+               {
+                       filtered_list = g_list_prepend(filtered_list, strinfo);
+               }
+
+               if (strinfo->src_addr==ip_src_rev
+                       && strinfo->src_port==port_src_rev
+                       && strinfo->dest_addr==ip_dst_rev
+                       && strinfo->dest_port==port_dst_rev)
+               {
+                       ++nfound;
+                       filtered_list = g_list_append(filtered_list, strinfo);
+                       if (ssrc_rev==0)
+                               ssrc_rev = strinfo->ssrc;
+               }
+
+               strinfo_list = g_list_next(strinfo_list);
+       }
+
+       /* if more than one reverse streams found, we let the user choose the right one */
+       if (nfound>1) {
+               rtpstream_dlg_show(filtered_list);
+               return;
+       }
+       else {
+               rtp_analysis(
+                       ip_src_fwd,
+                       port_src_fwd,
+                       ip_dst_fwd,
+                       port_dst_fwd,
+                       ssrc_fwd,
+                       ip_src_rev,
+                       port_src_rev,
+                       ip_dst_rev,
+                       port_dst_rev,
+                       ssrc_rev
+                       );
+       }
+}
+
+/****************************************************************************/
+static void
+rtp_analysis_init(char *dummy _U_)
+{
+       rtp_analysis_cb(NULL, NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_rtp_analysis(void)
+{
+       register_ethereal_tap("rtp", rtp_analysis_init);
+}
+
+void
+register_tap_menu_rtp_analysis(void)
+{
+       register_tap_menu_item("Statistics/RTP Streams/Analyse...",
+           rtp_analysis_cb, NULL, NULL);
+}
diff --git a/gtk/rtp_analysis.h b/gtk/rtp_analysis.h
new file mode 100644 (file)
index 0000000..ae5afc5
--- /dev/null
@@ -0,0 +1,50 @@
+/* rtp_analysis.h
+ * RTP analysis addition for ethereal
+ *
+ * $Id: rtp_analysis.h,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef RTP_ANALYSIS_H_INCLUDED
+#define RTP_ANALYSIS_H_INCLUDED
+
+#include <glib.h>
+
+void rtp_analysis(
+               guint32 ip_src_fwd,  /* network-order IPv4 address */
+               guint16 port_src_fwd,
+               guint32 ip_dst_fwd,  /* network-order IPv4 address */
+               guint16 port_dst_fwd,
+               guint32 ssrc_fwd,
+               guint32 ip_src_rev,  /* network-order IPv4 address */
+               guint16 port_src_rev,
+               guint32 ip_dst_rev,  /* network-order IPv4 address */
+               guint16 port_dst_rev,
+               guint32 ssrc_rev
+               );
+
+#endif /*RTP_ANALYSIS_H_INCLUDED*/
diff --git a/gtk/rtp_stream.c b/gtk/rtp_stream.c
new file mode 100644 (file)
index 0000000..b662cb6
--- /dev/null
@@ -0,0 +1,407 @@
+/* rtp_stream.c
+ * RTP streams summary addition for ethereal
+ *
+ * $Id: rtp_stream.c,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "rtp_stream.h"
+#include "rtp_stream_dlg.h"
+
+#include "globals.h"
+
+#include "tap.h"
+#include "register.h"
+#include "packet-rtp.h"
+
+#include "simple_dialog.h"
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+/****************************************************************************/
+/* the one and only global rtpstream_tapinfo_t structure */
+static rtpstream_tapinfo_t the_tapinfo_struct =
+       {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
+
+
+/****************************************************************************/
+/* GCompareFunc style comparison function for _rtp_stream_info */
+gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
+{
+       const struct _rtp_stream_info* a = aa;
+       const struct _rtp_stream_info* b = bb;
+
+       if (a==b)
+               return 0;
+       if (a==NULL || b==NULL)
+               return 1;
+       if ((a->src_addr == b->src_addr)
+               && (a->src_port == b->src_port)
+               && (a->dest_addr == b->dest_addr)
+               && (a->dest_port == b->dest_port)
+               && (a->ssrc == b->ssrc)
+               && (a->pt == b->pt))
+               return 0;
+       else
+               return 1;
+}
+
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+void rtpstream_reset(rtpstream_tapinfo_t *tapinfo _U_)
+{
+       GList* list;
+
+       if (tapinfo->mode == TAP_ANALYSE) {
+               /* free the data items first */
+               list = g_list_first(tapinfo->strinfo_list);
+               while (list)
+               {
+                       g_free(list->data);
+                       list = g_list_next(list);
+               }
+               g_list_free(tapinfo->strinfo_list);
+               tapinfo->strinfo_list = NULL;
+               tapinfo->nstreams = 0;
+               tapinfo->npackets = 0;
+       }
+
+       ++(tapinfo->launch_count);
+
+       return;
+}
+
+/****************************************************************************/
+/* redraw the output */
+void rtpstream_draw(rtpstream_tapinfo_t *tapinfo _U_)
+{
+/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
+       gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
+*/
+       rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
+       return;
+}
+
+
+/*
+* rtpdump file format
+*
+* The file starts with the tool to be used for playing this file,
+* the multicast/unicast receive address and the port.
+*
+* #!rtpplay1.0 224.2.0.1/3456\n
+*
+* This is followed by one binary header (RD_hdr_t) and one RD_packet_t
+* structure for each received packet.  All fields are in network byte
+* order.  We don't need the source IP address since we can do mapping
+* based on SSRC.  This saves (a little) space, avoids non-IPv4
+* problems and privacy/security concerns. The header is followed by
+* the RTP/RTCP header and (optionally) the actual payload.
+*/
+
+#define RTPFILE_VERSION "1.0"
+
+/*
+* Write a header to the current output file.
+* The header consists of an identifying string, followed
+* by a binary structure.
+*/
+static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
+{
+       guint32 start_sec;     /* start of recording (GMT) (seconds) */
+       guint32 start_usec;    /* start of recording (GMT) (microseconds)*/
+       guint32 source;        /* network source (multicast address) */
+       guint16 port;          /* UDP port */
+       guint16 padding;       /* 2 padding bytes */
+       
+       fprintf(file, "#!rtpplay%s %s/%d\n", RTPFILE_VERSION,
+               ip_to_str((guint8*) &strinfo->dest_addr),
+               strinfo->dest_port);
+
+       start_sec = g_htonl(strinfo->start_sec);
+       start_usec = g_htonl(strinfo->start_usec);
+       source = strinfo->src_addr; /* already is in network order */
+       port = g_htons(strinfo->src_port);
+       padding = 0;
+
+       fwrite(&start_sec, 4, 1, file);
+       fwrite(&start_usec, 4, 1, file);
+       fwrite(&source, 4, 1, file);
+       fwrite(&port, 2, 1, file);
+       fwrite(&padding, 2, 1, file);
+}
+
+/* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
+static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
+{
+       guint16 length;    /* length of packet, including this header (may 
+                            be smaller than plen if not whole packet recorded) */
+       guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
+       guint32 offset;    /* milliseconds since the start of recording */
+
+       length = g_htons(sample->header.frame_length + 8);
+       plen = g_htons(sample->header.frame_length);
+       offset = g_htonl(sample->header.rec_time);
+
+       fwrite(&length, 2, 1, file);
+       fwrite(&plen, 2, 1, file);
+       fwrite(&offset, 4, 1, file);
+       fwrite(sample->frame, sample->header.frame_length, 1, file);
+}
+
+
+/* utility function for writing a sample to file in RAS format */
+static
+void ras_write_sample(rtp_sample_t* sample, FILE* file)
+{
+       rtp_sample_header_t net_order_header;
+
+       net_order_header.rec_time = g_htonl(sample->header.rec_time);
+       net_order_header.frame_length = g_htons(sample->header.frame_length);
+
+       fwrite(&(net_order_header.rec_time), 1, 4, file);
+       fwrite(&(net_order_header.frame_length), 1, 2, file);
+       fwrite(sample->frame, 1, sample->header.frame_length, file);
+}
+
+
+/****************************************************************************/
+/* whenever a RTP packet is seen by the tap listener */
+int rtpstream_packet(rtpstream_tapinfo_t *tapinfo _U_, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo _U_)
+{
+       rtp_stream_info_t tmp_strinfo;
+       rtp_stream_info_t *strinfo = NULL;
+       GList* list;
+
+       rtp_sample_t sample;
+
+       /* we ignore packets that are not displayed */
+/*
+       if (pinfo->fd->flags.passed_dfilter == 0)
+               return 0;
+*/
+
+       /* gather infos on the stream this packet is part of */
+       g_memmove(&(tmp_strinfo.src_addr), pinfo->src.data, 4);
+       tmp_strinfo.src_port = pinfo->srcport;
+       g_memmove(&(tmp_strinfo.dest_addr), pinfo->dst.data, 4);
+       tmp_strinfo.dest_port = pinfo->destport;
+       tmp_strinfo.ssrc = rtpinfo->info_sync_src;
+       tmp_strinfo.pt = rtpinfo->info_payload_type;
+
+       if (tapinfo->mode == TAP_ANALYSE) {
+               /* check wether we already have a stream with these parameters in the list */
+               list = g_list_first(tapinfo->strinfo_list);
+               while (list)
+               {
+                       if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
+                       {
+                               strinfo = (rtp_stream_info_t*)(list->data);  /*found!*/
+                               break;
+                       }
+                       list = g_list_next(list);
+               }
+
+               /* not in the list? then create a new entry */
+               if (!strinfo) {
+                       tmp_strinfo.npackets = 0;
+                       tmp_strinfo.first_frame_num = pinfo->fd->num;
+                       tmp_strinfo.start_sec = pinfo->fd->abs_secs;
+                       tmp_strinfo.start_usec = pinfo->fd->abs_usecs;
+                       tmp_strinfo.tag_vlan_error = 0;
+                       tmp_strinfo.tag_diffserv_error = 0;
+                       tmp_strinfo.vlan_id = 0;
+                       strinfo = g_malloc(sizeof(rtp_stream_info_t));
+                       *strinfo = tmp_strinfo;  /* memberwise copy of struct */
+                       tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
+               }
+
+               /* increment the packets counter for this stream */
+               ++(strinfo->npackets);
+
+               /* increment the packets counter of all streams */
+               ++(tapinfo->npackets);
+       }
+       else if (tapinfo->mode == TAP_SAVE) {
+               if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
+/*                     sample.header.rec_time = pinfo->fd->abs_secs*1000 + pinfo->fd->abs_usecs;*/
+                       sample.header.rec_time = 
+                               (pinfo->fd->abs_usecs + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
+                               + (pinfo->fd->abs_secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
+                       sample.header.frame_length = rtpinfo->info_data_len;
+                       sample.frame = cfile.pd + pinfo->fd->pkt_len - rtpinfo->info_data_len;
+                       rtp_write_sample(&sample, tapinfo->save_file);
+/*                     ras_write_sample(&sample, tapinfo->save_file);*/
+               }
+       }
+       else if (tapinfo->mode == TAP_MARK) {
+               if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
+                       || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
+               {
+                       mark_frame(&cfile, pinfo->fd);
+               }
+       }
+
+       return 0;
+}
+
+/****************************************************************************/
+/* scan for RTP streams */
+void rtpstream_scan()
+{
+       gboolean was_registered = the_tapinfo_struct.is_registered;
+       if (!the_tapinfo_struct.is_registered)
+               register_tap_listener_rtp_stream();
+
+       the_tapinfo_struct.mode = TAP_ANALYSE;
+       redissect_packets(&cfile);
+
+       if (!was_registered)
+               remove_tap_listener_rtp_stream();
+}
+
+
+/****************************************************************************/
+/* save rtp dump of stream_fwd */
+void rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
+{
+       gboolean was_registered = the_tapinfo_struct.is_registered;
+       /* open file for saving */
+       the_tapinfo_struct.save_file = fopen(filename, "wb");
+       if (the_tapinfo_struct.save_file==NULL) {
+               return;
+       }
+
+       rtp_write_header(stream, the_tapinfo_struct.save_file);
+
+       if (!the_tapinfo_struct.is_registered)
+               register_tap_listener_rtp_stream();
+
+       the_tapinfo_struct.mode = TAP_SAVE;
+       the_tapinfo_struct.filter_stream_fwd = stream;
+       redissect_packets(&cfile);
+       the_tapinfo_struct.mode = TAP_ANALYSE;
+
+       if (!was_registered)
+               remove_tap_listener_rtp_stream();
+
+       /* XXX check for error at fclose? */
+       fclose(the_tapinfo_struct.save_file);
+}
+
+
+/****************************************************************************/
+/* mark packets in stream_fwd or stream_rev */
+void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
+{
+       gboolean was_registered = the_tapinfo_struct.is_registered;
+       if (!the_tapinfo_struct.is_registered)
+               register_tap_listener_rtp_stream();
+
+       the_tapinfo_struct.mode = TAP_MARK;
+       the_tapinfo_struct.filter_stream_fwd = stream_fwd;
+       the_tapinfo_struct.filter_stream_rev = stream_rev;
+       redissect_packets(&cfile);
+       the_tapinfo_struct.mode = TAP_ANALYSE;
+
+       if (!was_registered)
+               remove_tap_listener_rtp_stream();
+}
+
+
+/****************************************************************************/
+const rtpstream_tapinfo_t* rtpstream_get_info()
+{
+       return &the_tapinfo_struct;
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+
+/****************************************************************************/
+static void
+rtpstream_init_tap(char *dummy _U_)
+{
+       /* XXX: never called? */
+}
+
+
+/* XXX just copied from gtk/rpc_stat.c */
+void protect_thread_critical_region(void);
+void unprotect_thread_critical_region(void);
+
+/****************************************************************************/
+void
+remove_tap_listener_rtp_stream(void)
+{
+       if (the_tapinfo_struct.is_registered) {
+               protect_thread_critical_region();
+               remove_tap_listener(&the_tapinfo_struct);
+               unprotect_thread_critical_region();
+
+               the_tapinfo_struct.is_registered = FALSE;
+       }
+}
+
+
+/****************************************************************************/
+void
+register_tap_listener_rtp_stream(void)
+{
+       gchar filter_text[256];
+       GString *error_string;
+
+       if (!the_tapinfo_struct.is_registered) {
+               register_ethereal_tap("rtp", rtpstream_init_tap);
+
+               sprintf(filter_text, "rtp && ip && !icmp");
+
+               error_string = register_tap_listener("rtp", &the_tapinfo_struct,
+                       filter_text,
+                       (void*)rtpstream_reset, (void*)rtpstream_packet, (void*)rtpstream_draw);
+
+               if (error_string != NULL) {
+                       simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str);
+                       g_string_free(error_string, TRUE);
+                       exit(1);
+               }
+
+               the_tapinfo_struct.is_registered = TRUE;
+       }
+}
diff --git a/gtk/rtp_stream.h b/gtk/rtp_stream.h
new file mode 100644 (file)
index 0000000..3817268
--- /dev/null
@@ -0,0 +1,140 @@
+/* rtp_stream.h
+ * RTP streams summary addition for ethereal
+ *
+ * $Id: rtp_stream.h,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef RTP_STREAM_H_INCLUDED
+#define RTP_STREAM_H_INCLUDED
+
+#include <glib.h>
+#include <stdio.h>
+
+/****************************************************************************/
+/* type for storing rtp frame information */
+typedef struct st_rtp_sample_header {
+       guint32 rec_time;       /* miliseconds since start of recording */
+       guint16 frame_length;   /* number of bytes in *frame */
+} rtp_sample_header_t;
+
+/* type for storing rtp frame information */
+typedef struct st_rtp_sample {
+       rtp_sample_header_t header;  /* date and size */
+       char *frame;                 /* data bytes */
+} rtp_sample_t;
+
+typedef rtp_sample_t* rtp_sample_p;
+
+
+/* defines an rtp stream */
+typedef struct _rtp_stream_info {
+       guint32 src_addr;
+       guint16 src_port;
+       guint32 dest_addr;
+       guint16 dest_port;
+       guint32 ssrc;
+       guint8  pt;
+       guint32 npackets;
+
+       guint32 first_frame_num; /* frame number of first frame */
+       /* start of recording (GMT) of this stream */
+    guint32 start_sec;         /* seconds */
+    guint32 start_usec;        /* microseconds */
+       gboolean tag_vlan_error;
+       gboolean tag_diffserv_error;
+       guint16 vlan_id;
+
+} rtp_stream_info_t;
+
+
+/* tapping modes */
+typedef enum
+{
+  TAP_ANALYSE,
+  TAP_SAVE,
+  TAP_MARK
+} tap_mode_t;
+
+
+/* structure that holds the information about all detected streams */
+/* struct holding all information of the tap */
+typedef struct _rtpstream_tapinfo {
+       int     nstreams;       /* number of streams in the list */
+       GList*  strinfo_list;   /* list with all streams */
+       int     npackets;       /* total number of rtp packets of all streams */
+       /* used while tapping. user shouldnt modify these */
+       tap_mode_t mode;
+       rtp_stream_info_t* filter_stream_fwd;  /* used as filter in some tap modes */
+       rtp_stream_info_t* filter_stream_rev;  /* used as filter in some tap modes */
+       FILE*   save_file;
+       guint32 launch_count;   /* number of times the tap has been run */
+       gboolean is_registered; /* if the tap listener is currently registered or not */
+} rtpstream_tapinfo_t;
+
+
+/****************************************************************************/
+/* INTERFACE */
+
+/*
+* Registers the rtp_streams tap listener (if not already done).
+* From that point on, the RTP streams list will be updated with every redissection.
+* This function is also the entry point for the initialization routine of the tap system.
+* So whenever rtp_stream.c is added to the list of ETHEREAL_TAP_SRCs, the tap will be registered on startup.
+* If not, it will be registered on demand by the rtp_streams and rtp_analysis functions that need it.
+*/
+void register_tap_listener_rtp_stream(void);
+
+/*
+* Removes the rtp_streams tap listener (if not already done)
+* From that point on, the RTP streams list won't be updated any more.
+*/
+void remove_tap_listener_rtp_stream(void);
+
+/*
+* Retrieves a constant reference to the unique info structure of the rtp_streams tap listener.
+* The user should not modify the data pointed to.
+*/
+const rtpstream_tapinfo_t* rtpstream_get_info();
+
+/*
+* Scans all packets for RTP streams and updates the RTP streams list.
+* (redissects all packets)
+*/
+void rtpstream_scan();
+
+/*
+* Saves an RTP stream as raw data stream with timestamp information for later RTP playback.
+* (redissects all packets)
+*/
+void rtpstream_save(rtp_stream_info_t* stream, const gchar *filename);
+
+/*
+* Marks all packets belonging to either of stream_fwd or stream_rev.
+* (both can be NULL)
+* (redissects all packets)
+*/
+void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev);
+
+
+#endif /*RTP_STREAM_H_INCLUDED*/
diff --git a/gtk/rtp_stream_dlg.c b/gtk/rtp_stream_dlg.c
new file mode 100644 (file)
index 0000000..8811cf3
--- /dev/null
@@ -0,0 +1,806 @@
+/* rtp_stream_dlg.c
+ * RTP streams summary addition for ethereal
+ *
+ * $Id: rtp_stream_dlg.c,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "rtp_stream_dlg.h"
+#include "rtp_stream.h"
+#include "rtp_analysis.h"
+
+#include "globals.h"
+#include "epan/filesystem.h"
+
+#include "menu.h"
+#include "dlg_utils.h"
+#include "ui_util.h"
+#include "main.h"
+#include "compat_macros.h"
+
+#include <string.h>
+
+extern GtkWidget *main_display_filter_widget;
+
+
+/****************************************************************************/
+/*
+ * RTP Payload types
+ * Table B.2 / H.225.0
+ * Also RFC 1890, and
+ *
+ *     http://www.iana.org/assignments/rtp-parameters
+ */
+#define PT_PCMU                0       /* RFC 1890 */
+#define PT_1016                1       /* RFC 1890 */
+#define PT_G721                2       /* RFC 1890 */
+#define PT_GSM         3       /* RFC 1890 */
+#define PT_G723                4       /* From Vineet Kumar of Intel; see the Web page */
+#define PT_DVI4_8000   5       /* RFC 1890 */
+#define PT_DVI4_16000  6       /* RFC 1890 */
+#define PT_LPC         7       /* RFC 1890 */
+#define PT_PCMA                8       /* RFC 1890 */
+#define PT_G722                9       /* RFC 1890 */
+#define PT_L16_STEREO  10      /* RFC 1890 */
+#define PT_L16_MONO    11      /* RFC 1890 */
+#define PT_QCELP       12      /* Qualcomm Code Excited Linear Predictive coding? */
+#define PT_CN          13      /* RFC 3389 */
+#define PT_MPA         14      /* RFC 1890, RFC 2250 */
+#define PT_G728                15      /* RFC 1890 */
+#define PT_DVI4_11025  16      /* from Joseph Di Pol of Sun; see the Web page */
+#define PT_DVI4_22050  17      /* from Joseph Di Pol of Sun; see the Web page */
+#define PT_G729                18
+#define PT_CELB                25      /* RFC 2029 */
+#define PT_JPEG                26      /* RFC 2435 */
+#define PT_NV          28      /* RFC 1890 */
+#define PT_H261                31      /* RFC 2032 */
+#define PT_MPV         32      /* RFC 2250 */
+#define PT_MP2T                33      /* RFC 2250 */
+#define PT_H263                34      /* from Chunrong Zhu of Intel; see the Web page */
+
+static const value_string rtp_payload_type_vals[] =
+{
+       { PT_PCMU,      "ITU-T G.711 PCMU" },
+       { PT_1016,      "USA Federal Standard FS-1016" },
+       { PT_G721,      "ITU-T G.721" },
+       { PT_GSM,       "GSM 06.10" },
+       { PT_G723,      "ITU-T G.723" },
+       { PT_DVI4_8000, "DVI4 8000 samples/s" },
+       { PT_DVI4_16000, "DVI4 16000 samples/s" },
+       { PT_LPC,       "Experimental linear predictive encoding from Xerox PARC" },
+       { PT_PCMA,      "ITU-T G.711 PCMA" },
+       { PT_G722,      "ITU-T G.722" },
+       { PT_L16_STEREO, "16-bit uncompressed audio, stereo" },
+       { PT_L16_MONO,  "16-bit uncompressed audio, monaural" },
+       { PT_QCELP,     "Qualcomm Code Excited Linear Predictive coding" },
+       { PT_CN,        "Comfort noise" },
+       { PT_MPA,       "MPEG-I/II Audio"},
+       { PT_G728,      "ITU-T G.728" },
+       { PT_DVI4_11025, "DVI4 11025 samples/s" },
+       { PT_DVI4_22050, "DVI4 22050 samples/s" },
+       { PT_G729,      "ITU-T G.729" },
+       { PT_CELB,      "Sun CellB video encoding" },
+       { PT_JPEG,      "JPEG-compressed video" },
+       { PT_NV,        "'nv' program" },
+       { PT_H261,      "ITU-T H.261" },
+       { PT_MPV,       "MPEG-I/II Video"},
+       { PT_MP2T,      "MPEG-II transport streams"},
+       { PT_H263,      "ITU-T H.263" },
+       { 0,            NULL },
+};
+
+
+typedef const guint8 * ip_addr_p;
+
+static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse button";
+static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with SHIFT + left mouse button";
+
+/****************************************************************************/
+/* pointer to the one and only dialog window */
+static GtkWidget *rtp_stream_dlg = NULL;
+
+/* save as dialog box */
+static GtkWidget *rtpstream_save_dlg = NULL;
+static GtkWidget *clist = NULL;
+static GtkWidget *label_fwd = NULL;
+static GtkWidget *label_rev = NULL;
+
+static rtp_stream_info_t* selected_stream_fwd = NULL;  /* current selection */
+static rtp_stream_info_t* selected_stream_rev = NULL;  /* current selection for reversed */
+static GList *last_list = NULL;
+
+
+/****************************************************************************/
+/* append a line to clist */
+static void add_to_clist(rtp_stream_info_t* strinfo)
+{
+       gint added_row;
+       gchar *data[8];
+       gchar field[8][30];
+
+       data[0]=&field[0][0];
+       data[1]=&field[1][0];
+       data[2]=&field[2][0];
+       data[3]=&field[3][0];
+       data[4]=&field[4][0];
+       data[5]=&field[5][0];
+       data[6]=&field[6][0];
+       data[7]=&field[7][0];
+
+       g_snprintf(field[0], 20, "%s", ip_to_str((const guint8*)&(strinfo->src_addr)));
+       g_snprintf(field[1], 20, "%u", strinfo->src_port);
+       g_snprintf(field[2], 20, "%s", ip_to_str((const guint8*)&(strinfo->dest_addr)));
+       g_snprintf(field[3], 20, "%u", strinfo->dest_port);
+       g_snprintf(field[4], 20, "%u", strinfo->ssrc);
+       g_snprintf(field[5], 30, "%s", val_to_str(strinfo->pt, rtp_payload_type_vals,
+               "Unknown (%u)"));
+       g_snprintf(field[6], 20, "%u", strinfo->npackets);
+       /* XXX: Comment field is not used for the moment */
+/*     g_snprintf(field[7], 20, "%s", "");*/
+
+       added_row = gtk_clist_append(GTK_CLIST(clist), data);
+
+       /* set data pointer of last row to point to user data for that row */
+       gtk_clist_set_row_data(GTK_CLIST(clist), added_row, strinfo);
+}
+
+/****************************************************************************/
+static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+       /* Note that we no longer have a Save voice info dialog box. */
+       rtpstream_save_dlg = NULL;
+}
+
+/****************************************************************************/
+/* save in a file */
+static void save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer user_data _U_)
+{
+       gchar *g_dest;
+
+       if (!selected_stream_fwd)
+               return;
+
+       g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (rtpstream_save_dlg)));
+
+       /* Perhaps the user specified a directory instead of a file.
+       Check whether they did. */
+       if (test_for_directory(g_dest) == EISDIR) {
+               /* It's a directory - set the file selection box to display it. */
+               set_last_open_dir(g_dest);
+               g_free(g_dest);
+               gtk_file_selection_set_filename(GTK_FILE_SELECTION(rtpstream_save_dlg), last_open_dir);
+               return;
+       }
+
+       rtpstream_save(selected_stream_fwd, g_dest);
+
+       gtk_widget_destroy(GTK_WIDGET(rtpstream_save_dlg));
+}
+
+
+/****************************************************************************/
+/* CALLBACKS                                                                */
+/****************************************************************************/
+static void
+rtpstream_on_destroy                      (GtkObject       *object _U_,
+                                        gpointer         user_data _U_)
+{
+       /* Is there a save voice window open? */
+       if (rtpstream_save_dlg != NULL)
+               gtk_widget_destroy(rtpstream_save_dlg);
+
+       /* Note that we no longer have a "RTP Analyse" dialog box. */
+       rtp_stream_dlg = NULL;
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_unselect                  (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       selected_stream_fwd = NULL;
+       selected_stream_rev = NULL;
+       gtk_clist_unselect_all(GTK_CLIST(clist));
+       gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
+       gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
+}
+
+
+/****************************************************************************/
+/*
+static void
+rtpstream_on_goto                      (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       if (selected_stream_fwd)
+       {
+               goto_frame(&cfile, selected_stream_fwd->first_frame_num);
+       }
+}
+*/
+
+
+/****************************************************************************/
+static void
+rtpstream_on_save                      (GtkButton       *button _U_,
+                                        gpointer         data _U_)
+{
+       rtpstream_tapinfo_t* tapinfo = data;
+
+       GtkWidget *vertb;
+       GtkWidget *ok_bt;
+
+       if (!selected_stream_fwd)
+               return;
+
+       if (rtpstream_save_dlg != NULL) {
+               /* There's already a Save dialog box; reactivate it. */
+               reactivate_window(rtpstream_save_dlg);
+               return;
+       }
+
+       rtpstream_save_dlg = gtk_file_selection_new("Ethereal: Save selected stream in rtpdump ('-F dump') format");
+       gtk_signal_connect(GTK_OBJECT(rtpstream_save_dlg), "destroy",
+               GTK_SIGNAL_FUNC(save_stream_destroy_cb), NULL);
+
+       /* Container for each row of widgets */
+       vertb = gtk_vbox_new(FALSE, 0);
+       gtk_container_border_width(GTK_CONTAINER(vertb), 5);
+       gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(rtpstream_save_dlg)->action_area),
+               vertb, FALSE, FALSE, 0);
+       gtk_widget_show (vertb);
+
+       ok_bt = GTK_FILE_SELECTION(rtpstream_save_dlg)->ok_button;
+/*     OBJECT_SET_DATA(ok_bt, "user_data", tapinfo);*/
+
+       /* Connect the cancel_button to destroy the widget */
+       SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(rtpstream_save_dlg)->cancel_button,
+               "clicked", (GtkSignalFunc)gtk_widget_destroy,
+               rtpstream_save_dlg);
+
+       /* Catch the "key_press_event" signal in the window, so that we can catch
+       the ESC key being pressed and act as if the "Cancel" button had
+       been selected. */
+       dlg_set_cancel(rtpstream_save_dlg, GTK_FILE_SELECTION(rtpstream_save_dlg)->cancel_button);
+
+       gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
+               GTK_SIGNAL_FUNC(save_stream_ok_cb), tapinfo);
+
+       gtk_widget_show(rtpstream_save_dlg);
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_mark                      (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
+               return;
+       rtpstream_mark(selected_stream_fwd, selected_stream_rev);
+}
+
+
+#define MAX_FILTER_LENGTH 320
+
+/****************************************************************************/
+static void
+rtpstream_on_filter                    (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       gchar filter_string[MAX_FILTER_LENGTH] = "";
+       gchar filter_string_rev[MAX_FILTER_LENGTH] = "";
+
+       if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
+               return;
+
+       if (selected_stream_fwd)
+       {
+               g_snprintf(filter_string, MAX_FILTER_LENGTH,
+                       "(ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u && rtp.ssrc==%u)",
+                       ip_to_str((const guint8*)&(selected_stream_fwd->src_addr)),
+                       selected_stream_fwd->src_port,
+                       ip_to_str((const guint8*)&(selected_stream_fwd->dest_addr)),
+                       selected_stream_fwd->dest_port,
+                       selected_stream_fwd->ssrc);
+
+               if (selected_stream_rev)
+               {
+                       strcat(filter_string, " || ");
+               }
+       }
+
+       if (selected_stream_rev)
+       {
+               g_snprintf(filter_string_rev, MAX_FILTER_LENGTH,
+                       "(ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u && rtp.ssrc==%u)",
+                       ip_to_str((const guint8*)&(selected_stream_rev->src_addr)),
+                       selected_stream_rev->src_port,
+                       ip_to_str((const guint8*)&(selected_stream_rev->dest_addr)),
+                       selected_stream_rev->dest_port,
+                       selected_stream_rev->ssrc);
+               strcat(filter_string, filter_string_rev);
+       }
+
+       gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
+/*
+       filter_packets(&cfile, filter_string);
+       rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
+*/
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_close                     (GtkButton        *button _U_,
+                                        gpointer         user_data _U_)
+{
+       gtk_grab_remove(rtp_stream_dlg);
+       gtk_widget_destroy(rtp_stream_dlg);
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_analyse                   (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       guint32 ip_src_fwd = 0;
+       guint16 port_src_fwd = 0;
+       guint32 ip_dst_fwd = 0;
+       guint16 port_dst_fwd = 0;
+       guint32 ssrc_fwd = 0;
+       guint32 ip_src_rev = 0;
+       guint16 port_src_rev = 0;
+       guint32 ip_dst_rev = 0;
+       guint16 port_dst_rev = 0;
+       guint32 ssrc_rev = 0;
+
+       if (selected_stream_fwd) {
+               ip_src_fwd = selected_stream_fwd->src_addr;
+               port_src_fwd = selected_stream_fwd->src_port;
+               ip_dst_fwd = selected_stream_fwd->dest_addr;
+               port_dst_fwd = selected_stream_fwd->dest_port;
+               ssrc_fwd = selected_stream_fwd->ssrc;
+       }
+
+       if (selected_stream_rev) {
+               ip_src_rev = selected_stream_rev->src_addr;
+               port_src_rev = selected_stream_rev->src_port;
+               ip_dst_rev = selected_stream_rev->dest_addr;
+               port_dst_rev = selected_stream_rev->dest_port;
+               ssrc_rev = selected_stream_rev->ssrc;
+       }
+
+       rtp_analysis(
+               ip_src_fwd,
+               port_src_fwd,
+               ip_dst_fwd,
+               port_dst_fwd,
+               ssrc_fwd,
+               ip_src_rev,
+               port_src_rev,
+               ip_dst_rev,
+               port_dst_rev,
+               ssrc_rev
+               );
+}
+
+
+/****************************************************************************/
+/* This should be the callback function called upon a user-defined
+ * event "signal_rtpstream_update", but i didn't knoow how to do with GTK
+static void
+rtpstream_on_update                    (GtkButton       *button _U_,
+                                        gpointer         user_data _U_)
+{
+       rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
+}
+*/
+
+/****************************************************************************/
+/* when the user selects a row in the stream list */
+static void
+rtpstream_on_select_row(GtkCList *clist,
+                                            gint row _U_,
+                                            gint column _U_,
+                                            GdkEventButton *event _U_,
+                                            gpointer user_data _U_)
+{
+       gchar label_text[80];
+
+       /* update the labels */
+       if (event->state & GDK_SHIFT_MASK) {
+               selected_stream_rev = gtk_clist_get_row_data(GTK_CLIST(clist), row);
+               g_snprintf(label_text, 80, "Reverse: %s:%u -> %s:%u, SSRC=%u",
+                       ip_to_str((ip_addr_p)&selected_stream_rev->src_addr),
+                       selected_stream_rev->src_port,
+                       ip_to_str((ip_addr_p)&selected_stream_rev->dest_addr),
+                       selected_stream_rev->dest_port,
+                       selected_stream_rev->ssrc
+               );
+               gtk_label_set_text(GTK_LABEL(label_rev), label_text);
+       }
+       else {
+               selected_stream_fwd = gtk_clist_get_row_data(GTK_CLIST(clist), row);
+               g_snprintf(label_text, 80, "Forward: %s:%u -> %s:%u, SSRC=%u",
+                       ip_to_str((ip_addr_p)&selected_stream_fwd->src_addr),
+                       selected_stream_fwd->src_port,
+                       ip_to_str((ip_addr_p)&selected_stream_fwd->dest_addr),
+                       selected_stream_fwd->dest_port,
+                       selected_stream_fwd->ssrc
+               );
+               gtk_label_set_text(GTK_LABEL(label_fwd), label_text);
+       }
+
+/*
+       gtk_widget_set_sensitive(save_bt, TRUE);
+       gtk_widget_set_sensitive(filter_bt, TRUE);
+       gtk_widget_set_sensitive(mark_bt, TRUE);
+*/
+       /* TODO: activate other buttons when implemented */
+}
+
+
+/****************************************************************************/
+/* INTERFACE                                                                */
+/****************************************************************************/
+
+static void rtpstream_dlg_create (void)
+{
+       /* these are global static now:
+       GtkWidget *clist = NULL;
+       GtkWidget *label_fwd = NULL;
+       GtkWidget *label_rev = NULL;
+       */
+       GtkWidget *rtpstream_dlg_w;
+       GtkWidget *dialog_vbox1;
+       GtkWidget *vbox1;
+       GtkWidget *label10;
+       GtkWidget *scrolledwindow1;
+       GtkWidget *label2;
+       GtkWidget *label3;
+       GtkWidget *label4;
+       GtkWidget *label5;
+       GtkWidget *label6;
+       GtkWidget *label7;
+       GtkWidget *label8;
+/*     GtkWidget *label9;*/
+       GtkWidget *dialog_action_area1;
+       GtkWidget *hbuttonbox2;
+/*     GtkWidget *bt_goto;*/
+       GtkWidget *bt_unselect;
+       GtkWidget *bt_save;
+       GtkWidget *bt_frames;
+       GtkWidget *bt_filter;
+       GtkWidget *bt_analyse;
+       GtkWidget *bt_close;
+       
+       rtpstream_dlg_w = gtk_dialog_new ();
+       gtk_window_set_title (GTK_WINDOW (rtpstream_dlg_w), "Ethereal: RTP Streams");
+       
+       dialog_vbox1 = GTK_DIALOG (rtpstream_dlg_w)->vbox;
+       gtk_widget_show (dialog_vbox1);
+       
+       vbox1 = gtk_vbox_new (FALSE, 0);
+       gtk_widget_ref (vbox1);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "vbox1", vbox1,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (vbox1);
+       gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1, TRUE, TRUE, 0);
+       gtk_container_set_border_width (GTK_CONTAINER (vbox1), 8);
+       
+       label10 = gtk_label_new ("Detected RTP streams. Choose one for forward and reverse direction for analysis");
+       gtk_widget_ref (label10);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label10", label10,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label10);
+       gtk_box_pack_start (GTK_BOX (vbox1), label10, FALSE, FALSE, 0);
+       gtk_widget_set_usize (label10, -2, 32);
+       
+       scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
+       gtk_widget_ref (scrolledwindow1);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "scrolledwindow1", scrolledwindow1,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (scrolledwindow1);
+       gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0);
+       
+       clist = gtk_clist_new (7); /* defines number of columns */
+       gtk_widget_ref (clist);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "clist", clist,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (clist);
+       gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist);
+       gtk_widget_set_usize (clist, 640, 200);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 0, 100);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 1, 50);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 2, 100);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 3, 50);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 4, 80);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 5, 118);
+       gtk_clist_set_column_width (GTK_CLIST (clist), 6, 60);
+/*     gtk_clist_set_column_width (GTK_CLIST (clist), 7, 51);*/
+       gtk_clist_column_titles_show (GTK_CLIST (clist));
+       
+       label2 = gtk_label_new ("Src IP addr");
+       gtk_widget_ref (label2);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label2", label2,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label2);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 0, label2);
+       
+       label3 = gtk_label_new ("Src port");
+       gtk_widget_ref (label3);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label3", label3,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label3);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 1, label3);
+       
+       label4 = gtk_label_new ("Dest IP addr");
+       gtk_widget_ref (label4);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label4", label4,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label4);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 2, label4);
+       
+       label5 = gtk_label_new ("Dest port");
+       gtk_widget_ref (label5);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label5", label5,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label5);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 3, label5);
+       
+       label6 = gtk_label_new ("SSRC");
+       gtk_widget_ref (label6);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label6", label6,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label6);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 4, label6);
+       gtk_widget_set_usize (label6, 80, -2);
+       
+       label7 = gtk_label_new ("Payload");
+       gtk_widget_ref (label7);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label7", label7,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label7);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 5, label7);
+       
+       label8 = gtk_label_new ("Packets");
+       gtk_widget_ref (label8);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label8", label8,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label8);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 6, label8);
+/*     
+       label9 = gtk_label_new ("Comment");
+       gtk_widget_ref (label9);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label9", label9,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label9);
+       gtk_clist_set_column_widget (GTK_CLIST (clist), 7, label9);
+*/     
+       label_fwd = gtk_label_new (FWD_LABEL_TEXT);
+       gtk_widget_ref (label_fwd);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label_fwd", label_fwd,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label_fwd);
+       gtk_box_pack_start (GTK_BOX (vbox1), label_fwd, FALSE, FALSE, 0);
+       gtk_label_set_justify (GTK_LABEL (label_fwd), GTK_JUSTIFY_LEFT);
+       
+       label_rev = gtk_label_new (REV_LABEL_TEXT);
+       gtk_widget_ref (label_rev);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "label_rev", label_rev,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (label_rev);
+       gtk_box_pack_start (GTK_BOX (vbox1), label_rev, FALSE, FALSE, 0);
+       gtk_label_set_justify (GTK_LABEL (label_rev), GTK_JUSTIFY_LEFT);
+       
+       dialog_action_area1 = GTK_DIALOG (rtpstream_dlg_w)->action_area;
+       gtk_widget_show (dialog_action_area1);
+       gtk_container_set_border_width (GTK_CONTAINER (dialog_action_area1), 10);
+       
+       hbuttonbox2 = gtk_hbutton_box_new ();
+       gtk_widget_ref (hbuttonbox2);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "hbuttonbox2", hbuttonbox2,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (hbuttonbox2);
+       gtk_box_pack_start (GTK_BOX (dialog_action_area1), hbuttonbox2, FALSE, FALSE, 0);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_END);
+       gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox2), 0);
+       
+       bt_unselect = gtk_button_new_with_label ("Unselect");
+       gtk_widget_ref (bt_unselect);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_unselect", bt_unselect,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_unselect);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_unselect);
+       GTK_WIDGET_SET_FLAGS (bt_unselect, GTK_CAN_DEFAULT);
+/*     
+       bt_goto = gtk_button_new_with_label ("Go to Frame");
+       gtk_widget_ref (bt_goto);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_goto", bt_goto,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_goto);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_goto);
+       GTK_WIDGET_SET_FLAGS (bt_goto, GTK_CAN_DEFAULT);
+*/     
+       bt_save = gtk_button_new_with_label ("Save as...");
+       gtk_widget_ref (bt_save);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_save", bt_save,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_save);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_save);
+       GTK_WIDGET_SET_FLAGS (bt_save, GTK_CAN_DEFAULT);
+       
+       bt_frames = gtk_button_new_with_label ("Mark frames");
+       gtk_widget_ref (bt_frames);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_frames", bt_frames,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_frames);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_frames);
+       GTK_WIDGET_SET_FLAGS (bt_frames, GTK_CAN_DEFAULT);
+       
+       bt_filter = gtk_button_new_with_label ("Set filter");
+       gtk_widget_ref (bt_filter);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_filter", bt_filter,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_filter);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_filter);
+       GTK_WIDGET_SET_FLAGS (bt_filter, GTK_CAN_DEFAULT);
+       
+       bt_analyse = gtk_button_new_with_label ("Analyse");
+       gtk_widget_ref (bt_analyse);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_analyse", bt_analyse,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_analyse);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_analyse);
+       GTK_WIDGET_SET_FLAGS (bt_analyse, GTK_CAN_DEFAULT);
+       
+       bt_close = gtk_button_new_with_label ("Close");
+       gtk_widget_ref (bt_close);
+       gtk_object_set_data_full (GTK_OBJECT (rtpstream_dlg_w), "bt_close", bt_close,
+               (GtkDestroyNotify) gtk_widget_unref);
+       gtk_widget_show (bt_close);
+       gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_close);
+       GTK_WIDGET_SET_FLAGS (bt_close, GTK_CAN_DEFAULT);
+       
+       gtk_signal_connect (GTK_OBJECT (rtpstream_dlg_w), "destroy",
+               GTK_SIGNAL_FUNC (rtpstream_on_destroy),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (clist), "select_row",
+               GTK_SIGNAL_FUNC (rtpstream_on_select_row),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (bt_unselect), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_unselect),
+               NULL);
+/*
+       gtk_signal_connect (GTK_OBJECT (bt_goto), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_goto),
+               NULL);
+*/
+       gtk_signal_connect (GTK_OBJECT (bt_save), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_save),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (bt_frames), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_mark),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (bt_filter), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_filter),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (bt_analyse), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_analyse),
+               NULL);
+       gtk_signal_connect (GTK_OBJECT (bt_close), "clicked",
+               GTK_SIGNAL_FUNC (rtpstream_on_close),
+               NULL);
+/* XXX: see rtpstream_on_update for comment
+       gtk_signal_connect (GTK_OBJECT (top_level), "signal_rtpstream_update",
+               GTK_SIGNAL_FUNC (rtpstream_on_update),
+               NULL);
+*/
+       
+       if (clist) {
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_LEFT);
+               gtk_clist_set_column_justification(GTK_CLIST(clist), 6, GTK_JUSTIFY_RIGHT);
+/*             gtk_clist_set_column_justification(GTK_CLIST(clist), 7, GTK_JUSTIFY_CENTER);*/
+       }
+
+       rtpstream_on_unselect(NULL, NULL);
+
+       rtp_stream_dlg = rtpstream_dlg_w;
+}
+
+
+/****************************************************************************/
+/* PUBLIC                                                                   */
+/****************************************************************************/
+
+/****************************************************************************/
+/* update the contents of the dialog box clist */
+/* list: pointer to list of rtp_stream_info_t* */
+void rtpstream_dlg_update(GList *list)
+{
+       if (rtp_stream_dlg != NULL) {
+               gtk_clist_clear(GTK_CLIST(clist));
+
+               list = g_list_first(list);
+               while (list)
+               {
+                       add_to_clist((rtp_stream_info_t*)(list->data));
+                       list = g_list_next(list);
+               }
+
+               rtpstream_on_unselect(NULL, NULL);
+       }
+
+       last_list = list;
+}
+
+
+/****************************************************************************/
+/* update the contents of the dialog box clist */
+/* list: pointer to list of rtp_stream_info_t* */
+void rtpstream_dlg_show(GList *list)
+{
+       if (rtp_stream_dlg != NULL) {
+               /* There's already a dialog box; reactivate it. */
+               reactivate_window(rtp_stream_dlg);
+               /* Another list since last call? */
+               if (list != last_list) {
+                       rtpstream_dlg_update(list);
+               }
+       }
+       else {
+               /* Create and show the dialog box */
+               rtpstream_dlg_create();
+               rtpstream_dlg_update(list);
+               gtk_widget_show(rtp_stream_dlg);
+       }
+}
+
+
+/****************************************************************************/
+/* entry point when called via the GTK menu */
+void rtpstream_launch(GtkWidget *w _U_, gpointer data _U_)
+{
+       /* Show the dialog box */
+       rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
+}
+
+/****************************************************************************/
+void
+register_tap_menu_rtp_stream(void)
+{
+       register_tap_menu_item("Statistics/RTP Streams/Show All...",
+           rtpstream_launch, NULL, NULL);
+}
diff --git a/gtk/rtp_stream_dlg.h b/gtk/rtp_stream_dlg.h
new file mode 100644 (file)
index 0000000..69f7dd9
--- /dev/null
@@ -0,0 +1,45 @@
+/* rtp_stream_dlg.h
+ * RTP streams summary addition for ethereal
+ *
+ * $Id: rtp_stream_dlg.h,v 1.1 2003/09/24 07:48:11 guy Exp $
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef RTP_STREAM_DLG_H_INCLUDED
+#define RTP_STREAM_DLG_H_INCLUDED
+
+#include <gtk/gtk.h>
+
+/*
+* Create or reactivate the rtp streams dialog box.
+* list: pointer to list of rtp_stream_info_t*
+*/
+void rtpstream_dlg_show(GList *list);
+
+/*
+* Update the contents of the dialog box clist with that of list.
+* list: pointer to list of rtp_stream_info_t*
+*/
+void rtpstream_dlg_update(GList *list);
+
+#endif /*RTP_STREAM_DLG_H_INCLUDED*/
diff --git a/gtk/tap_rtp.c b/gtk/tap_rtp.c
deleted file mode 100644 (file)
index 0682b54..0000000
+++ /dev/null
@@ -1,1761 +0,0 @@
-/*
- * tap_rtp.c
- *
- * $Id: tap_rtp.c,v 1.17 2003/09/19 07:24:39 guy Exp $
- *
- * RTP analysing addition for ethereal
- *
- * Copyright 2003, Iskratel, Ltd, Kranj
- * By Miha Jemec <m.jemec@iskratel.si>
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
- * Copyright 1998 Gerald Combs
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
- * This tap works as follows:
- * When the user clicks on the RTP analisys button, we first check if it is a RTP packet.
- * If yes we store the SSRC, ip, and port values. Then the tap is registered and the 
- * redissect_packets() routine is called. So we go through all the RTP packets and search 
- * for SSRC of reversed connection (it has inversed socket parameters). If more than one 
- * is found a window is displayed where the user can select the appropriate from the list.
- * Rigth now we have the information about the converstion we are looking for (both SSRC). 
- * The redissect_packets() routine is called again. This time whenever a RTP packet with
- * matching SSRC values arrives, we store all the information we need (number, sequence
- * number, arrival time, ...) and compute the delay, jitter and wrong sequence number.
- * We add this values to CList. If the RTP packet carries voice in g711 alaw or ulaw, we
- * also store this voice information in a temp file. Window is displayed.
- * Then three buttons are available: Close, Refresh and Save voice.
- * The Refresh button calls the redissect_packets() routine again. It goes through the packets
- * again and does all the calculation again (if capturing in real time this means that some
- * more packets could come and can be computed in statistic). It also writes the sound
- * data again.
- * The Save voice button opens the dialog where we can choose the file name, format (not yet)
- * and direction we want to save. Currently it works only with g711 alaw and ulaw, and if the
- * length of captured packets is equal the length of packets on wire 
- *
- * To do:
- * - Support for saving voice in more different formats and with more different codecs:
- *   Since this should be portable to all OS, there is only possibility to save the 
- *   voice in a file and not play it directly through the sound card. There are enough 
- *   players on all platforms, that are doing right this. What about the format? 
- *   Currently there is only support for saving as an .au file (ulaw, 8000 Hz, 8bit)
- *   There are many players for this format on all platforms (for example Windows Media Player
- *   under Windows, command play under Linux). Support will be added for wav format and 
- *   possibility to save with two channels (separate channel for each direction)
- *
- * - Support for more codecs. Now you can save voice only if the codec is g.711 alaw or ulaw.
- *
- * - right now, the reversed connection must have the same (only inversed) ip and port numbers.
- *   I think that there is no reason that in special cases the reversed connection would not use 
- *   some different port or even the IP combination (please correct me if I am wrong). 
- *   So this will be added soon.
- *
- * - some more statistics (delay and jitter distribution)
- *
- * - GTK2 implementation
- *
- * - grammar correction
- * 
- * - some more testing (other OS)
- *
- * XXX Problems: 
- *
- * - problem with statistics for lost (late, duplicated) packets. How to make the statistic 
- *   more resistant to special (bizarre) arrival of sequence numbers
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdio.h>
-
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
-#include <gtk/gtk.h>
-#include "globals.h"
-#include <string.h>
-#include "epan/packet_info.h"
-#include <epan/epan_dissect.h>
-#include <epan/filesystem.h>
-#include "../tap.h"
-#include "../register.h"
-#include "../packet-rtp.h"
-#include "file_dlg.h"
-#include "dlg_utils.h"
-#include "ui_util.h"
-#include "simple_dialog.h"
-#include "menu.h"
-#include "main.h"
-#include <math.h>
-#include "progress_dlg.h"
-#include "compat_macros.h"
-#include "../g711.h"
-#include "../util.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-
-#ifdef HAVE_IO_H
-#include <io.h>        /* open/close on win32 */
-#endif
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-static GtkWidget *rtp_w = NULL;
-static GtkWidget *save_voice_as_w = NULL;
-static GtkWidget *save_csv_as_w = NULL;
-static GtkWidget *main_vb;
-static GtkWidget *clist;
-static GtkWidget *clist_r;
-static GtkWidget *max;
-static GtkWidget *max_r;
-
-static char f_tempname[128+1], r_tempname[128+1];
-
-/* type of error when saving voice in a file didn't succeed */
-typedef enum {
-       TAP_RTP_WRONG_CODEC,
-       TAP_RTP_WRONG_LENGTH,
-       TAP_RTP_PADDING_ERROR,
-       TAP_RTP_FILE_OPEN_ERROR,
-       TAP_RTP_NO_DATA
-} error_type_t; 
-
-typedef enum {
-       FIRST_PACKET,
-       MARK_SET,
-       NORMAL_PACKET
-} packet_type;
-
-/* structure that holds the information about the forward and reversed connection */
-struct _info_direction {
-       gboolean first_packet;
-       guint16 seq_num;
-       guint32 timestamp;
-       guint32 delta_timestamp;
-       double delay;
-       double jitter;
-       double time;
-       double start_time;
-       double max_delay;
-       guint32 max_nr;
-       guint16 start_seq_nr;
-       guint16 stop_seq_nr;
-       guint32 total_nr;
-       guint32 sequence;
-       gboolean under;
-       gint cycles;
-       FILE *fp;
-       guint32 count;
-       error_type_t error_type;
-       gboolean saved;
-};
-
-/* structure that holds general information about the connection 
- * and structures for both directions */
-typedef struct _info_stat {
-       gchar source[16];
-       gchar destination[16];
-       guint16 srcport;
-       guint16 dstport;
-       guint32 ssrc_forward;
-       guint32 ssrc_reversed;
-       guint32 *ssrc_tmp;
-       gboolean search_ssrc;
-       guint reversed_ip;
-       guint reversed_ip_and_port;
-       struct _info_direction forward;
-       struct _info_direction reversed;
-} info_stat;
-
-int do_calculation(gboolean direc, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo); 
-static gboolean copy_file(gchar *, /*gint,*/ gint, void *);
-
-/* when there is a [re]reading of packet's */
-static void
-rtp_reset(void *prs)
-{
-  info_stat *rs=prs;
-
-  rs->forward.first_packet = TRUE;
-  rs->reversed.first_packet = TRUE;
-  rs->forward.max_delay = 0;
-  rs->reversed.max_delay = 0;
-  rs->forward.delay = 0;
-  rs->reversed.delay = 0;
-  rs->forward.jitter = 0;
-  rs->reversed.jitter = 0;
-  rs->forward.timestamp = 0;
-  rs->reversed.timestamp = 0;
-  rs->forward.max_nr = 0;
-  rs->reversed.max_nr = 0;
-  rs->forward.total_nr = 0;
-  rs->reversed.total_nr = 0;
-  rs->forward.sequence = 0;
-  rs->reversed.sequence = 0;
-  rs->forward.start_seq_nr = 0;
-  rs->reversed.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
-  rs->forward.stop_seq_nr = 0;
-  rs->reversed.stop_seq_nr = 0;
-  rs->forward.cycles = 0;
-  rs->reversed.cycles = 0;
-  rs->forward.under = FALSE;
-  rs->reversed.under = FALSE;
-  rs->forward.saved = FALSE;
-  rs->reversed.saved = FALSE;
-  rs->forward.start_time = 0;
-  rs->reversed.start_time = 0;
-  rs->forward.time = 0;
-  rs->reversed.time = 0;
-  rs->forward.count = 0;
-  rs->reversed.count = 0;
-  /* XXX check for error at fclose? */
-  rs->forward.fp = freopen(f_tempname, "wb", rs->forward.fp); 
-  if (rs->forward.fp == NULL)
-       rs->forward.error_type = TAP_RTP_FILE_OPEN_ERROR;
-  rs->reversed.fp = freopen(r_tempname, "wb", rs->reversed.fp);
-  if (rs->reversed.fp == NULL)
-       rs->reversed.error_type = TAP_RTP_FILE_OPEN_ERROR;
-  return;
-}
-
-/* here we can redraw the output */
-/* not used yet */
-static void rtp_draw(void *prs _U_)
-{
-       return;
-}
-
-/* when we are finished with redisection, we add the label for the statistic */
-static void draw_stat(void *prs)
-{
-       info_stat *rs=prs;
-       gchar label_max[200];
-       guint32 f_expected = (rs->forward.stop_seq_nr + rs->forward.cycles*65536) 
-                                                       - rs->forward.start_seq_nr + 1;
-       guint32 r_expected = (rs->reversed.stop_seq_nr + rs->reversed.cycles*65536) 
-                                                       - rs->reversed.start_seq_nr + 1;
-       gint32 f_lost = f_expected - rs->forward.total_nr;
-       gint32 r_lost = r_expected - rs->reversed.total_nr;
-
-       g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n"
-               "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"  
-               "   Sequence error = %u",
-                       rs->forward.max_delay, rs->forward.max_nr, rs->forward.total_nr, 
-                                               f_expected, f_lost, rs->forward.sequence);
-
-       gtk_label_set_text(GTK_LABEL(max), label_max);
-
-       g_snprintf(label_max, 199, "Max delay = %f sec at packet nr. %u \n\n"
-               "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"
-               "   Sequence error = %u",
-                        rs->reversed.max_delay, rs->reversed.max_nr, rs->reversed.total_nr, 
-                                               r_expected, r_lost, rs->reversed.sequence);
-
-       gtk_label_set_text(GTK_LABEL(max_r), label_max);
-
-       /* could be done somewhere else, but can be here as well */
-       /* if this is true, then we don't have any reversed connection, so the error type
-        * will be no data. This applies only the reversed connection */
-       if (rs->reversed_ip_and_port == 0)
-               rs->reversed.error_type = TAP_RTP_NO_DATA;
-
-       return ;
-}
-
-/* append a line to clist */
-/* XXX is there a nicer way to make these assignements? */
-static void add_to_clist(gboolean forward, guint32 number, guint16 seq_num, 
-                               double delay, double jitter, gboolean status, gboolean marker, gchar *timeStr, guint32 pkt_len)
-{
-       gchar *data[8];
-       gchar field[8][30];
-
-       data[0]=&field[0][0];
-       data[1]=&field[1][0];
-       data[2]=&field[2][0];
-       data[3]=&field[3][0];
-       data[4]=&field[4][0];
-       data[5]=&field[5][0];
-       data[6]=&field[6][0];
-       data[7]=&field[7][0];
-
-       g_snprintf(field[0], 20, "%u", number);
-       g_snprintf(field[1], 20, "%u", seq_num);
-       g_snprintf(field[2], 20, "%f", delay);
-       g_snprintf(field[3], 20, "%f", jitter);
-       g_snprintf(field[4], 20, "%s", marker? "SET" : "");
-       g_snprintf(field[5], 29, "%s", status? "OK" : "NOK - Wrong sequence nr.");
-       g_snprintf(field[6], 32, "%s", timeStr);
-       g_snprintf(field[7], 20, "%u", pkt_len);
-
-       gtk_clist_append(GTK_CLIST(forward? clist : clist_r), data);
-
-}
-
-/* whenever a RTP packet is seen by the tap listener */
-/* this function works as follows:
- * 1) packets that are not displayed are ignored
- *     return
- * 2) are we searching what could be the reversed connection (looking for reversed SSRC)
- *     if yes, do the parameters match (inversed IP and port combination from the forward one)?
- *             if yes, do we already have this SSRC stored
- *                     if not store it
- * 3) if not, is current packet matching the forward direction
- *     if yes, call the function that does the calculation and saves the voice info
- * 4) if not, is current packet matching the reversed connection
- *     if yes, call the function that does the calculation and saves the voice info
- */
-static int rtp_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, void *vpri)
-{
-       info_stat *rs=prs;
-       struct _rtp_info *pri=vpri;
-       guint i;
-
-       /* we ignore packets that are not displayed */
-       if (pinfo->fd->flags.passed_dfilter == 0)
-               return 0;
-
-       /* are we looking for the SSRC of the reversed connection? */
-       if (rs->search_ssrc != FALSE) {
-               /* XXX what should be the rules for reversed connection? 
-                * 1. It should should have same inversed IP and port numbers
-                * 2. If none are found, only inversed IP's - is this possible?
-                * 3. If none are found, there isn't any reversed connection 
-                * XXX is it possible that the conversation is not P2P? 
-                * Curretly it works only if it matches the number 1. */
-
-               /* have we found inverse parameters? */
-               if ( strcmp(ip_to_str(pinfo->src.data), rs->destination) == 0  && 
-                               strcmp( ip_to_str(pinfo->dst.data), rs->source) == 0 ) {
-
-                       /* do the ports also match? */
-                       if ((rs->srcport == pinfo->destport) && (rs->dstport == pinfo->srcport)) {
-                               /* ok, the ip and port combination does match 
-                                * do we already have this ssrc stored */
-                               for(i=0; i< rs->reversed_ip_and_port; i++) {
-                                       if (pri->info_sync_src == *(rs->ssrc_tmp+i) ) 
-                                               return 0;
-                               }
-                               
-                               /* no, we found new ssrc, let's store it */
-                               rs->ssrc_tmp = (guint32*)g_realloc(rs->ssrc_tmp, 
-                                                               (i+1)*sizeof(guint32));
-                               *(rs->ssrc_tmp+i) = pri->info_sync_src;
-                               rs->reversed_ip_and_port++;
-                               return 0;
-                       }
-                       /* no, only ip addresses match */
-                       /* XXX not implemented yet */
-                       else {
-                               rs->reversed_ip++;
-                               return 0;
-                       }
-
-               }
-       }
-       
-       /* ok, we are not looking for SSRC of the reversed connection */
-       /* is it the forward direction?  */
-       else if (rs->ssrc_forward == pri->info_sync_src)  {
-               if (rs->forward.first_packet != FALSE) 
-                       /* first argument is the direction TRUE == forward */
-                       return do_calculation(TRUE, FIRST_PACKET, &rs->forward, pri, pinfo);
-               else if (pri->info_marker_set != FALSE)
-                       return do_calculation(TRUE, MARK_SET, &rs->forward, pri, pinfo);
-               else
-                       return do_calculation(TRUE, NORMAL_PACKET, &rs->forward, pri, pinfo);
-       }
-       /* is it the reversed direction? */
-       else if (rs->ssrc_reversed == pri->info_sync_src) {
-               if (rs->reversed.first_packet != FALSE) 
-                       return do_calculation(FALSE, FIRST_PACKET, &rs->reversed, pri, pinfo);
-               else if (pri->info_marker_set != FALSE)
-                       return do_calculation(FALSE, MARK_SET, &rs->reversed, pri, pinfo);
-               else
-                       return do_calculation(FALSE, NORMAL_PACKET, &rs->reversed, pri, pinfo);
-       }
-
-       return 0;
-}
-
-
-int do_calculation(gboolean direc, packet_type pkt_type, void *ptrs, void *vpri, void *vpinfo) {
-
-       struct _info_direction *ptr=ptrs;
-       struct _rtp_info *pri=vpri;
-       packet_info *pinfo = vpinfo;
-       guint i;
-       double current_time;
-       double current_jitter;
-       guint8 *data;
-       gint16 tmp;
-       guint16 msecs;
-       gchar timeStr[32];
-
-       struct tm *tm_tmp;
-       time_t then;
-
-       then = pinfo->fd->abs_secs;
-       msecs = (guint16)(pinfo->fd->abs_usecs/1000);
-       
-       tm_tmp = localtime(&then);
-       snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
-           tm_tmp->tm_mon + 1,
-           tm_tmp->tm_mday,
-           tm_tmp->tm_year + 1900,
-           tm_tmp->tm_hour,
-           tm_tmp->tm_min,
-           tm_tmp->tm_sec,
-           msecs);
-       /* store the current time and calculate the current jitter */
-       current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
-       current_jitter = ptr->jitter + ( fabs (current_time - (ptr->time) - 
-               ((double)(pri->info_timestamp)-(double)(ptr->timestamp))/8000)- ptr->jitter)/16;
-       ptr->delay =  current_time-(ptr->time);
-
-       /* We have 3 possibilities:
-        *  is this the first packet we got in this direction? */
-       if (pkt_type == FIRST_PACKET) {
-               ptr->first_packet = FALSE;
-               ptr->start_seq_nr = pri->info_seq_num;
-               ptr->start_time = current_time;
-               add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, 0,
-                                pri->info_marker_set? TRUE: FALSE, TRUE, FALSE,
-                                timeStr, pinfo->fd->pkt_len);
-               if (ptr->fp == NULL) {
-                       ptr->saved = FALSE;
-                        ptr->error_type = TAP_RTP_FILE_OPEN_ERROR;
-                }
-               else
-                       ptr->saved = TRUE;
-       }
-       /* or is it a packet with the mark bit set? */
-       else if (pkt_type == MARK_SET) {
-               ptr->delta_timestamp = pri->info_timestamp - ptr->timestamp;
-               add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, current_time - (ptr->time),
-                        current_jitter, ptr->seq_num+1 == pri->info_seq_num? TRUE: FALSE, TRUE, timeStr, pinfo->fd->pkt_len);
-       }
-       /* if neither then it is a "normal" packet pkt_type == NORMAL_PACKET */
-       else {
-               if (ptr->delay > ptr->max_delay) {
-                       ptr->max_delay = ptr->delay;
-                       ptr->max_nr = pinfo->fd->num;
-               }
-               add_to_clist(direc, pinfo->fd->num, pri->info_seq_num, current_time -(ptr->time),
-                        current_jitter , ptr->seq_num+1 == pri->info_seq_num?TRUE:FALSE, FALSE, timeStr, pinfo->fd->pkt_len);
-       }
-
-       /* When calculating expected rtp packets the seq number can wrap around
-        * so we have to count the number of cycles
-        * Variable cycles counts the wraps around in forwarding connection and
-        * under is flag that indicates where we are
-        *
-        * XXX how to determine number of cycles with all possible lost, late
-        * and duplicated packets without any doubt? It seems to me, that
-        * because of all possible combination of late, duplicated or lost
-        * packets, this can only be more or less good approximation
-        *
-        * There are some combinations (rare but theoretically possible),
-        * where below code won't work correctly - statistic may be wrong then.
-        */
-
-       /* so if the current sequence number is less than the start one
-        * we assume, that there is another cycle running */
-       if ((pri->info_seq_num < ptr->start_seq_nr) && (ptr->under == FALSE)){
-               ptr->cycles++;
-               ptr->under = TRUE;
-       }
-       /* what if the start seq nr was 0? Then the above condition will never
-        * be true, so we add another condition. XXX The problem would arise 
-        * if one of the packets with seq nr 0 or 65535 would be lost or late */
-       else if ((pri->info_seq_num == 0) && (ptr->stop_seq_nr == 65535) &&
-                                                       (ptr->under == FALSE)){
-               ptr->cycles++;
-               ptr->under = TRUE;
-       }
-       /* the whole round is over, so reset the flag */
-       else if ((pri->info_seq_num > ptr->start_seq_nr) && (ptr->under != FALSE)) {
-                       ptr->under = FALSE;
-        }
-
-       /* Since it is difficult to count lost, duplicate or late packets separately, 
-        * we would like to know at least how many times the sequence number was not ok */
-
-       /* if the current seq number equals the last one or if we are here for 
-        * the first time, then it is ok, we just store the current one as the last one */
-       if ( ( ptr->seq_num+1 == pri->info_seq_num) || (pkt_type == FIRST_PACKET) )
-               ptr->seq_num = pri->info_seq_num;
-       /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
-       else if ( (ptr->seq_num == 65535) && (pri->info_seq_num == 0) )
-               ptr->seq_num = pri->info_seq_num;
-       /* lost packets */
-       else if (ptr->seq_num+1 < pri->info_seq_num) {
-               ptr->seq_num = pri->info_seq_num;
-               ptr->sequence++;
-       }
-       /* late or duplicated */
-       else if (ptr->seq_num+1 > pri->info_seq_num)
-               ptr->sequence++;
-
-       ptr->time = current_time;
-       ptr->timestamp = pri->info_timestamp;
-       ptr->stop_seq_nr = pri->info_seq_num;
-       ptr->total_nr++;
-
-       /* save the voice information */
-       /* if there was already an error, we quit */
-        if (ptr->saved == FALSE)
-               return 0;
-
-       /* if the captured length and packet length aren't equal, we quit
-         * because there is some information missing */
-        if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
-               ptr->saved = FALSE;
-               ptr->error_type = TAP_RTP_WRONG_LENGTH;
-               return 0;
-       }
-
-       /* if padding bit is set, but the padding count is bigger
-        * then the whole RTP data - error with padding count */
-        if ( (pri->info_padding_set != FALSE) &&
-                               (pri->info_padding_count > pri->info_payload_len) ) {
-               ptr->saved = FALSE;
-                ptr->error_type = TAP_RTP_PADDING_ERROR;
-                return 0;
-        }
-
-       /* do we need to insert some silence? */
-       if (pkt_type == MARK_SET) {
-               /* the amount of silence should be the difference between
-                 * the last timestamp and the current one minus x
-                 * x should equal the amount of information in the last frame
-                 * XXX not done yet */
-               for(i=0; i < (ptr->delta_timestamp - pri->info_payload_len -
-                                               pri->info_padding_count); i++) {
-                       tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
-                       fwrite(&tmp, 2, 1, ptr->fp);
-                       ptr->count++;
-               }
-               fflush(ptr->fp);
-       }
-
-       /* is it the ulaw? */
-       if (pri->info_payload_type == 0) {
-               /* we put the pointer at the beggining of the RTP data, that is
-                * at the end of the current frame minus the length of the
-                * padding count minus length of the RTP data */
-               data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len);
-               for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) {
-                       tmp = (gint16 )ulaw2linear((unsigned char)*data);
-                       fwrite(&tmp, 2, 1, ptr->fp);
-                       ptr->count++;
-               }
-               fflush(ptr->fp);
-               ptr->saved = TRUE;
-               return 0;
-       }
-
-       /* alaw? */
-       else if (pri->info_payload_type == 8) {
-               data = cfile.pd + (pinfo->fd->pkt_len - pri->info_payload_len);
-               for(i=0; i < (pri->info_payload_len - pri->info_padding_count); i++, data++) {
-                       tmp = (gint16 )alaw2linear((unsigned char)*data);
-                       fwrite(&tmp, 2, 1, ptr->fp);
-                       ptr->count++;
-               }
-               fflush(ptr->fp);
-               ptr->saved = TRUE;
-               return 0;
-       }
-
-       /* unsupported codec or XXX other error */
-       else {
-               ptr->saved = FALSE;
-               ptr->error_type = TAP_RTP_WRONG_CODEC;
-               return 0;
-       }
-}
-
-/* XXX just copied from gtk/rpc_stat.c */
-void protect_thread_critical_region(void);
-void unprotect_thread_critical_region(void);
-
-/* here we close the rtp analysis dialog window and remove the tap listener */
-static void rtp_destroy_cb(GtkWidget *win _U_, gpointer data _U_)
-{
-  info_stat *rs=(info_stat *)data;
-
-  protect_thread_critical_region();
-  remove_tap_listener(rs);
-  unprotect_thread_critical_region();
-
-  /* xxx is this enough? */
-  g_free(rs->ssrc_tmp);
-  g_free(rs);
-  if (rs->forward.fp != NULL)
-       fclose(rs->forward.fp);
-  if (rs->reversed.fp != NULL)
-       fclose(rs->reversed.fp);
-  remove(f_tempname);
-  remove(r_tempname);
-
-  /* Is there a save voice window open? */
-  if (save_voice_as_w != NULL)
-       gtk_widget_destroy(save_voice_as_w);
-
-  /* Note that we no longer have a "RTP Analyse" dialog box. */
-  rtp_w = NULL;
-}
-
-/* when the close button in rtp window was clicked */
-/* it seems to me that rtp_destroy_cb is automatically called, so we don't
- * need to do the g_free... and rtp_w = NULL ... */
-static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w)
-{
-    gtk_grab_remove(GTK_WIDGET(parent_w));
-    gtk_widget_destroy(GTK_WIDGET(parent_w));
-}
-
-/* we search the rtp.ssrc node here (thanks to Guy Harris - code here is magic for me */
-static guint32 process_node(proto_item *ptree_node, header_field_info *hfinformation) 
-{
-  field_info            *finfo;
-  proto_item            *proto_sibling_node;
-  header_field_info     *hfssrc;
-  guint32 ssrc;
-
-  finfo = PITEM_FINFO(ptree_node);
-
-  if (hfinformation==(finfo->hfinfo)) {
-       hfssrc = proto_registrar_get_byname("rtp.ssrc");
-       if (hfssrc == NULL)
-               return 0;
-       for(ptree_node=g_node_first_child(ptree_node); ptree_node!=NULL; 
-                               ptree_node=g_node_next_sibling(ptree_node)) {
-               finfo=PITEM_FINFO(ptree_node);
-               if (hfssrc==finfo->hfinfo) {
-                       ssrc = fvalue_get_integer(finfo->value);
-                       return ssrc;
-               }
-               }
-  }
-
-  proto_sibling_node = g_node_next_sibling(ptree_node);
-
-  if (proto_sibling_node) {
-       ssrc = process_node(proto_sibling_node, hfinformation);
-       return ssrc;
-  }
-  else
-       return 0;
-}
-
-/* here we search the rtp protocol */
-static guint32 process_tree(proto_tree *protocol_tree)
-{
-  proto_item      *ptree_node;
-  header_field_info     *hfinformation;
-
-  hfinformation = proto_registrar_get_byname("rtp");
-  if (hfinformation == NULL)
-       return 0;
-
-  ptree_node = g_node_first_child(protocol_tree);
-  if (!ptree_node)
-       return 0;
-
-  return process_node(ptree_node, hfinformation);
-}
-
-/* when we want to update the information */
-static void refresh_cb(GtkWidget *w _U_, void *pri)
-{
-  info_stat *rs=pri;
-
-  gtk_clist_clear(GTK_CLIST(clist));
-  gtk_clist_clear(GTK_CLIST(clist_r));
-  redissect_packets(&cfile);
-  draw_stat(rs);
-}
-
-/* when we want to save the information */
-static void save_csv_as_ok_cb(GtkWidget *ok_bt, gpointer fs)
-{
-  gchar *g_dest;
-  GtkWidget *rev, *forw, *both;
-  info_stat *rs;
-
-  FILE *fp;
-  char *columnText;
-  int i,j;
-  
-  g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
-
-  /* Perhaps the user specified a directory instead of a file.
-     Check whether they did. */
-  if (test_for_directory(g_dest) == EISDIR) {
-       /* It's a directory - set the file selection box to display it. */
-       set_last_open_dir(g_dest);
-       g_free(g_dest);
-       gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
-       return;
-  }
-
-  rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
-  forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
-  both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
-  rs = (info_stat *)OBJECT_GET_DATA(ok_bt, "info_stat");
-
-  if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
-       fp = fopen(g_dest, "w");
-
-       if (GTK_TOGGLE_BUTTON(both)->active) {
-               fprintf(fp, "Forward\n");
-       }
-
-       for(j = 0; j < GTK_CLIST(clist)->columns; j++) {
-         if (j == 0) {
-           fprintf(fp,"%s",GTK_CLIST(clist)->column[j].title);
-         } else {
-           fprintf(fp,",%s",GTK_CLIST(clist)->column[j].title);
-         }
-       }
-       fprintf(fp,"\n");
-       for (i = 0; i < GTK_CLIST(clist)->rows; i++) {
-         for(j = 0; j < GTK_CLIST(clist)->columns; j++) {
-               gtk_clist_get_text(GTK_CLIST(clist),i,j,&columnText);
-               if (j == 0) {
-                 fprintf(fp,"%s",columnText);
-               } else {
-                 fprintf(fp,",%s",columnText);
-               }
-         }
-         fprintf(fp,"\n");
-       }
-       fclose(fp);
-  }
-
-  if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
-       
-       if (GTK_TOGGLE_BUTTON(both)->active) {
-               fp = fopen(g_dest, "a");
-               fprintf(fp, "\nReverse\n");
-       } else {
-               fp = fopen(g_dest, "w");
-       }
-       for(j = 0; j < GTK_CLIST(clist_r)->columns; j++) {
-         if (j == 0) {
-           fprintf(fp,"%s",GTK_CLIST(clist_r)->column[j].title);
-         } else {
-           fprintf(fp,",%s",GTK_CLIST(clist_r)->column[j].title);
-         }
-       }
-       fprintf(fp,"\n");
-       for (i = 0; i < GTK_CLIST(clist_r)->rows; i++) {
-         for(j = 0; j < GTK_CLIST(clist_r)->columns; j++) {
-               gtk_clist_get_text(GTK_CLIST(clist_r),i,j,&columnText);
-               if (j == 0) {
-                 fprintf(fp,"%s",columnText);
-               } else {
-                 fprintf(fp,",%s",columnText);
-               }
-         }
-         fprintf(fp,"\n");
-       }
-       fclose(fp);
-  }
-  /* XXX I get GTK warning (sometimes?)!!! */
-  gtk_widget_destroy(GTK_WIDGET(save_csv_as_w));
-}
-
-static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
-{
-  save_csv_as_w = NULL;
-}
-
-/* when the user wants to save the csv information in a file */
-static void save_csv_as_cb(GtkWidget *w _U_, gpointer data)
-{
-  info_stat *rs=(info_stat *)data;
-
-  GtkWidget *vertb;
-  GtkWidget *table1;
-  GtkWidget *label_format;
-  GtkWidget *channels_label;
-  GSList *channels_group = NULL;
-  GtkWidget *forward_rb;
-  GtkWidget *reversed_rb;
-  GtkWidget *both_rb;
-  GtkWidget *ok_bt;
-
-  if (save_csv_as_w != NULL) {
-       /* There's already a Save CSV info dialog box; reactivate it. */
-       reactivate_window(save_csv_as_w);
-       return;
-  }
-  
-  save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
-  gtk_signal_connect(GTK_OBJECT(save_csv_as_w), "destroy",
-       GTK_SIGNAL_FUNC(save_csv_as_destroy_cb), NULL);
-
-  /* Container for each row of widgets */
-  vertb = gtk_vbox_new(FALSE, 0);
-  gtk_container_border_width(GTK_CONTAINER(vertb), 5);
-  gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_csv_as_w)->action_area),
-    vertb, FALSE, FALSE, 0);
-  gtk_widget_show (vertb);
-
-  table1 = gtk_table_new (2, 4, FALSE);
-  gtk_widget_show (table1);
-  gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
-  gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
-  gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
-
-  label_format = gtk_label_new ("Format: Comma Separated Values");
-  gtk_widget_show (label_format);
-  gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
-                    (GtkAttachOptions) (GTK_FILL),
-                    (GtkAttachOptions) (0), 0, 0);
-
-
-  channels_label = gtk_label_new ("Channels:");
-  gtk_widget_show (channels_label);
-  gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
-                               (GtkAttachOptions) (GTK_FILL),
-                               (GtkAttachOptions) (0), 0, 0);
-  gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
-
-  forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
-  gtk_widget_show (forward_rb);
-  gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
-  gtk_widget_show (reversed_rb);
-  gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  both_rb = gtk_radio_button_new_with_label (channels_group, "both");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
-  gtk_widget_show (both_rb);
-  gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
-
-  ok_bt = GTK_FILE_SELECTION(save_csv_as_w)->ok_button;
-  OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
-  OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
-  OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
-  OBJECT_SET_DATA(ok_bt, "info_stat", rs);
-
-  /* Connect the cancel_button to destroy the widget */
-  SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(save_csv_as_w)->cancel_button,
-                        "clicked", (GtkSignalFunc)gtk_widget_destroy,
-                        save_csv_as_w);
-
-  /* Catch the "key_press_event" signal in the window, so that we can catch
-     the ESC key being pressed and act as if the "Cancel" button had
-     been selected. */
-  dlg_set_cancel(save_csv_as_w, GTK_FILE_SELECTION(save_csv_as_w)->cancel_button);
-  
-  gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
-               GTK_SIGNAL_FUNC(save_csv_as_ok_cb), save_csv_as_w);
-  
-  gtk_widget_show(save_csv_as_w);
-}
-
-static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
-{
-  /* Note that we no longer have a Save voice info dialog box. */
-  save_voice_as_w = NULL;
-}
-
-/* the user wants to save in a file */
-/* XXX support for different formats is currently commented out */
-static void save_voice_as_ok_cb(GtkWidget *ok_bt, gpointer fs)
-{
-  gchar *g_dest;
-  /*GtkWidget *wav, *au, *sw;*/
-  GtkWidget *rev, *forw, *both;
-  info_stat *rs;
-  gint channels /*, format*/;
-
-  g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
-
-  /* Perhaps the user specified a directory instead of a file.
-     Check whether they did. */
-  if (test_for_directory(g_dest) == EISDIR) {
-       /* It's a directory - set the file selection box to display it. */
-       set_last_open_dir(g_dest);
-       g_free(g_dest);
-       gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
-       return;
-  }
-
-  /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
-  au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
-  sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
-  rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
-  forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
-  both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
-  rs = (info_stat *)OBJECT_GET_DATA(ok_bt, "info_stat");
-
-  /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
-   * we don't support that codec. So we pop up a warning. Maybe it would be better to
-   * disable the ok button or disable the buttons for direction if only one is not ok. The
-   * problem is if we open the save voice dialog and then click the refresh button and maybe 
-   * the state changes, so we can't save anymore. In this case we should be able to update
-   * the buttons. For now it is easier if we put the warning when the ok button is pressed.
-   */
-
-  /* we can not save in both dirctions */
-  if ((rs->forward.saved == FALSE) && (rs->reversed.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
-       /* there are many combinations here, we just exit when first matches */
-       if ((rs->forward.error_type == TAP_RTP_WRONG_CODEC) || 
-                                       (rs->reversed.error_type == TAP_RTP_WRONG_CODEC))
-               simple_dialog(ESD_TYPE_CRIT, NULL, 
-                       "Can't save in a file: Unsupported codec!");
-       else if ((rs->forward.error_type == TAP_RTP_WRONG_LENGTH) || 
-                                       (rs->reversed.error_type == TAP_RTP_WRONG_LENGTH))
-               simple_dialog(ESD_TYPE_CRIT, NULL, 
-                       "Can't save in a file: Wrong length of captured packets!");
-       else if ((rs->forward.error_type == TAP_RTP_PADDING_ERROR) || 
-                                       (rs->reversed.error_type == TAP_RTP_PADDING_ERROR))
-               simple_dialog(ESD_TYPE_CRIT, NULL, 
-                       "Can't save in a file: RTP data with padding!");
-       else  
-               simple_dialog(ESD_TYPE_CRIT, NULL, 
-                       "Can't save in a file: File I/O problem!");
-       return;
-  }
-  /* we can not save forward direction */
-  else if ((rs->forward.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
-                                               (GTK_TOGGLE_BUTTON (both)->active))) {  
-       if (rs->forward.error_type == TAP_RTP_WRONG_CODEC)
-                simple_dialog(ESD_TYPE_CRIT, NULL, 
-               "Can't save forward direction in a file: Unsupported codec!");
-        else if (rs->forward.error_type == TAP_RTP_WRONG_LENGTH)
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save forward direction in a file: Wrong length of captured packets!");
-        else if (rs->forward.error_type == TAP_RTP_PADDING_ERROR)
-                simple_dialog(ESD_TYPE_CRIT, NULL, 
-               "Can't save forward direction in a file: RTP data with padding!");
-        else
-                simple_dialog(ESD_TYPE_CRIT, NULL, 
-               "Can't save forward direction in a file: File I/O problem!");
-       return;
-  }
-  /* we can not save reversed direction */
-  else if ((rs->reversed.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
-                                               (GTK_TOGGLE_BUTTON (both)->active))) {  
-       if (rs->reversed.error_type == TAP_RTP_WRONG_CODEC)
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save reversed direction in a file: Unsupported codec!");
-        else if (rs->reversed.error_type == TAP_RTP_WRONG_LENGTH)
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save reversed direction in a file: Wrong length of captured packets!");
-        else if (rs->reversed.error_type == TAP_RTP_PADDING_ERROR)
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save reversed direction in a file: RTP data with padding!");
-        else if (rs->reversed.error_type == TAP_RTP_NO_DATA)
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save reversed direction in a file: No RTP data!");
-       else
-                simple_dialog(ESD_TYPE_CRIT, NULL,
-                "Can't save reversed direction in a file: File I/O problem!");
-        return;
-  }
-
-  /*if (GTK_TOGGLE_BUTTON (wav)->active)
-       format = 1;
-  else if (GTK_TOGGLE_BUTTON (au)->active)
-       format = 2;
-  else if (GTK_TOGGLE_BUTTON (sw)->active)
-       format = 3;*/
-
-  if (GTK_TOGGLE_BUTTON (rev)->active)
-       channels = 2;
-  else if (GTK_TOGGLE_BUTTON (both)->active)
-       channels = 3;
-  else 
-       channels = 1;
-
-  if(!copy_file(g_dest, channels/*, format*/, rs)) {
-       simple_dialog(ESD_TYPE_CRIT, NULL, "An error occured while saving voice in a file!");
-       return;
-  }
-
-  /* XXX I get GTK warning (sometimes?)!!! */
-  gtk_widget_destroy(GTK_WIDGET(save_voice_as_w));
-}
-
-/* when the user wants to save the voice information in a file */
-/* XXX support for different formats is currently commented out */
-static void save_voice_as_cb(GtkWidget *w _U_, gpointer data)
-{
-  info_stat *rs=(info_stat *)data;
-
-  GtkWidget *vertb;
-  GtkWidget *table1;
-  GtkWidget *label_format;
-  GtkWidget *channels_label;
-  /*GSList *format_group = NULL;*/
-  GSList *channels_group = NULL;
-  GtkWidget *forward_rb;
-  GtkWidget *reversed_rb;
-  GtkWidget *both_rb;
-  /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
-  GtkWidget *ok_bt;
-
-  /* if we can't save in a file: wrong codec, cut packets or other errors */
-  /* shold the error arise here or later when you click ok button ? 
-   * if we do it here, then we must disable the refresh button, so we don't do it here */
-
-  if (save_voice_as_w != NULL) {
-       /* There's already a Save voice info dialog box; reactivate it. */
-       reactivate_window(save_voice_as_w);
-       return;
-  }
-  
-  save_voice_as_w = gtk_file_selection_new("Ethereal: Save Voice Data As");
-  gtk_signal_connect(GTK_OBJECT(save_voice_as_w), "destroy",
-       GTK_SIGNAL_FUNC(save_voice_as_destroy_cb), NULL);
-
-  /* Container for each row of widgets */
-  vertb = gtk_vbox_new(FALSE, 0);
-  gtk_container_border_width(GTK_CONTAINER(vertb), 5);
-  gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(save_voice_as_w)->action_area),
-    vertb, FALSE, FALSE, 0);
-  gtk_widget_show (vertb);
-
-  table1 = gtk_table_new (2, 4, FALSE);
-  gtk_widget_show (table1);
-  gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
-  gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
-  gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
-
-  label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
-  gtk_widget_show (label_format);
-  gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
-                    (GtkAttachOptions) (GTK_FILL),
-                    (GtkAttachOptions) (0), 0, 0);
-
-  /* we support .au - ulaw*/ 
-/*  wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
-  format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
-  gtk_widget_show (wav_rb);
-  gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
-                    (GtkAttachOptions) (GTK_FILL),
-                    (GtkAttachOptions) (0), 0, 0);
-
-  sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit  ");
-  format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
-  gtk_widget_show (sw_rb);
-  gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
-                    (GtkAttachOptions) (GTK_FILL),
-                    (GtkAttachOptions) (0), 0, 0);
-  au_rb = gtk_radio_button_new_with_label (format_group, ".au");
-  format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
-  gtk_widget_show (au_rb);
-  gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
-                    (GtkAttachOptions) (GTK_FILL),
-                    (GtkAttachOptions) (0), 0, 0);
- */ 
-
-  channels_label = gtk_label_new ("Channels:");
-  gtk_widget_show (channels_label);
-  gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
-                               (GtkAttachOptions) (GTK_FILL),
-                               (GtkAttachOptions) (0), 0, 0);
-  gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
-
-  forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
-  gtk_widget_show (forward_rb);
-  gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
-  gtk_widget_show (reversed_rb);
-  gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  both_rb = gtk_radio_button_new_with_label (channels_group, "both");
-  channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
-  gtk_widget_show (both_rb);
-  gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
-                       (GtkAttachOptions) (GTK_FILL),
-                       (GtkAttachOptions) (0), 0, 0);
-
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
-
-  /* if one direction is nok we don't allow saving 
-  XXX this is not ok since the user can click the refresh button and cause changes
-  but we can not update this window. So we move all the decision on the time the ok
-  button is clicked
-  if (rs->forward.saved == FALSE) {
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
-       gtk_widget_set_sensitive(forward_rb, FALSE);
-       gtk_widget_set_sensitive(both_rb, FALSE);
-  }
-  else if (rs->reversed.saved == FALSE) {
-       gtk_widget_set_sensitive(reversed_rb, FALSE);
-       gtk_widget_set_sensitive(both_rb, FALSE);
-  }
-  */
-
-  ok_bt = GTK_FILE_SELECTION(save_voice_as_w)->ok_button;
-  /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
-  OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
-  OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
-  OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
-  OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
-  OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
-  OBJECT_SET_DATA(ok_bt, "info_stat", rs);
-
-  /* Connect the cancel_button to destroy the widget */
-  SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(save_voice_as_w)->cancel_button,
-                        "clicked", (GtkSignalFunc)gtk_widget_destroy,
-                        save_voice_as_w);
-
-  /* Catch the "key_press_event" signal in the window, so that we can catch
-     the ESC key being pressed and act as if the "Cancel" button had
-     been selected. */
-  dlg_set_cancel(save_voice_as_w, GTK_FILE_SELECTION(save_voice_as_w)->cancel_button);
-  
-  gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
-               GTK_SIGNAL_FUNC(save_voice_as_ok_cb), save_voice_as_w);
-  
-  gtk_widget_show(save_voice_as_w);
-}
-
-/* all the graphics on the window is done here */
-static void add_rtp_notebook(void *pri) 
-{
-  info_stat *rs=pri;
-
-  GtkWidget *notebook, *page, *page_r, *label, *label1, *label2, *label3;
-  GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
-  GtkWidget *box4, *voice_bt, *refresh_bt, *close_bn, *csv_bt;
-  
-  gchar *titles[8] =  {"Packet nr.", "Sequence",  "Delay (s)", "Jitter (s)", "Marker", "Status", "Date", "Length"};
-  gchar label_forward[150];
-  gchar label_reverse[150];
-
-  g_snprintf(label_forward, 149, 
-               "Analysing connection from  %s port %u  to  %s port %u   SSRC = %u\n", 
-               rs->source, rs->srcport, rs->destination, rs->dstport, rs->ssrc_forward);
-  g_snprintf(label_reverse, 149,
-               "Analysing connection from  %s port %u  to  %s port %u   SSRC = %u\n", 
-               rs->destination, rs->dstport, rs->source, rs->srcport, rs->ssrc_reversed);
-
-  gtk_widget_destroy(main_vb);
-  main_vb = gtk_vbox_new(FALSE, 3);
-  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
-  gtk_widget_show(main_vb);
-
-  /* Start a nootbook for flipping between sets of changes */
-  notebook = gtk_notebook_new();
-  gtk_container_add(GTK_CONTAINER(main_vb), notebook);
-  gtk_object_set_data(GTK_OBJECT(rtp_w), "notebook", notebook);
-
-  /* page for forward connection */
-  page = gtk_vbox_new(FALSE, 5);
-  gtk_container_set_border_width(GTK_CONTAINER(page), 20);
-
-  /* scrolled window */
-  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
-  gtk_widget_set_usize(scrolled_window, 600, 200);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), 
-                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-
-  /* direction label */
-  label1 = gtk_label_new(label_forward);
-  gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);
-
-  /* place for some statistics */
-  max = gtk_label_new("\n\n");
-  gtk_box_pack_end(GTK_BOX(page), max, FALSE, FALSE, 5);
-
-  /* clist for the information */
-  clist = gtk_clist_new_with_titles(8, titles);
-  gtk_widget_show(clist);
-  gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
-  gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
-
-  /* and the label */
-  label = gtk_label_new("     Forward Direction     ");
-  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
-
-  /* Hide date and length column */
-  gtk_clist_set_column_visibility(GTK_CLIST(clist), 6, FALSE);
-  gtk_clist_set_column_visibility(GTK_CLIST(clist), 7, FALSE);
-
-  /* column width and justification */
-  gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist), 1, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist), 2, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist), 3, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist), 4, 40);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_CENTER);
-
-  /* same page for reversed connection */
-  page_r = gtk_vbox_new(FALSE, 5);
-  gtk_container_set_border_width(GTK_CONTAINER(page_r), 20);
-  scrolled_window_r = gtk_scrolled_window_new(NULL, NULL);
-  gtk_widget_set_usize(scrolled_window_r, 600, 200);
-  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window_r), 
-                               GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-  label3 = gtk_label_new(label_reverse);
-  gtk_box_pack_start(GTK_BOX(page_r), label3, FALSE, FALSE, 0);
-  max_r = gtk_label_new("\n\n");
-  gtk_box_pack_end(GTK_BOX(page_r), max_r, FALSE, FALSE, 5);
-  clist_r = gtk_clist_new_with_titles(8, titles);
-  gtk_widget_show(clist_r);
-  gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_r);
-  gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
-  label2 = gtk_label_new("     Reversed Direction     ");
-  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label2);
-
-  /* Hide date and length column */
-  gtk_clist_set_column_visibility(GTK_CLIST(clist_r), 6, FALSE);
-  gtk_clist_set_column_visibility(GTK_CLIST(clist_r), 7, FALSE);
-
-  /* column width and justification */
-  gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist_r), 1, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist_r), 2, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist_r), 3, 80);
-  gtk_clist_set_column_width(GTK_CLIST(clist_r), 4, 40);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 0, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 1, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 2, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 3, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 4, GTK_JUSTIFY_CENTER);
-  gtk_clist_set_column_justification(GTK_CLIST(clist_r), 5, GTK_JUSTIFY_CENTER);
-
-  /* page for help&about or future 
-  page_help = gtk_hbox_new(FALSE, 5);
-  label4 = gtk_label_new("     Future    ");
-  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label4);
-  frame = gtk_frame_new("");
-  text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
-  gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
-  gtk_container_add(GTK_CONTAINER(frame), text);
-  gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
-  gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
-*/
-  /* show all notebooks */
-  gtk_widget_show_all(notebook);
-
-  /* and the buttons */
-  box4 = gtk_hbutton_box_new();
-  gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, TRUE, 0);
-  gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
-  gtk_button_box_set_layout(GTK_BUTTON_BOX(box4), GTK_BUTTONBOX_SPREAD);
-  gtk_widget_show(box4);
-
-  voice_bt = gtk_button_new_with_label("Save voice data as...");
-  gtk_container_add(GTK_CONTAINER(box4), voice_bt);
-  gtk_widget_show(voice_bt);
-  gtk_signal_connect(GTK_OBJECT(voice_bt), "clicked",
-               GTK_SIGNAL_FUNC(save_voice_as_cb), rs);
-
-  refresh_bt = gtk_button_new_with_label("Refresh");
-  gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
-  gtk_widget_show(refresh_bt);
-  gtk_signal_connect(GTK_OBJECT(refresh_bt), "clicked",
-               GTK_SIGNAL_FUNC(refresh_cb), rs);
-
-  csv_bt = gtk_button_new_with_label("Save As CSV...");
-  gtk_container_add(GTK_CONTAINER(box4), csv_bt);
-  gtk_widget_show(csv_bt);
-  gtk_signal_connect(GTK_OBJECT(csv_bt), "clicked",
-               GTK_SIGNAL_FUNC(save_csv_as_cb), rs);
-
-  close_bn = gtk_button_new_with_label("Close");
-  gtk_container_add(GTK_CONTAINER(box4), close_bn);
-  gtk_widget_show(close_bn);
-  gtk_signal_connect(GTK_OBJECT(close_bn), "clicked",
-               GTK_SIGNAL_FUNC(rtp_destroy), GTK_OBJECT(rtp_w));
-
-  redissect_packets(&cfile);
-
-  draw_stat(rs);
-}
-
-
-/* when we click on the selected row it copies that ssrc value into ssrc_reversed */
-static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column, 
-                                               GdkEventButton *event _U_, gpointer data)
-{
-  info_stat *rs=(info_stat *)data;
-  gchar *text;
-
-  gtk_clist_get_text(GTK_CLIST(clist_r), row, column, &text);
-  /* XXX is this strtoul portable for guint32? */
-  rs->ssrc_reversed = strtoul(text, (char **)NULL, 10);
-  return;
-}
-
-/* when we click apply button in ssrc reversed dialog */
-static void apply_selected_ssrc(GtkWidget *w _U_, gpointer data)
-{
-  info_stat *rs=(info_stat *)data;
-  add_rtp_notebook(rs);
-}
-
-/* this function goes through all the packets that have the same ip and port combination 
- * (only inversed) as the forward direction (XXX what if the reversed direction doesn't use 
- * the same ports???) and looks for different SSRC values. This can happen if you capture
- * two RTP conversations one after another from the same pair of phones (PC's). 
- * Both have same IP's and can also have same port numbers, so they (should) differ only 
- * in SSRC values. In such case we get a list of ssrc values and we have to choose the right 
- * one from the list. If there is only one or none, we do it automatically */ 
-static void get_reversed_ssrc(void *prs)
-{
-       info_stat *ri = prs;
-       GtkWidget *scroll_r, *clist_r, *ok_bt, *label, *label2, *label1, *main_hbnbox;
-       gchar temp[150];
-       guint i;
-
-       switch(ri->reversed_ip_and_port)
-       {
-               /* in case we haven't found any reversed ssrc */
-               /* XXX in this case we could look for the inversed IP only */
-               case 0: {
-                       ri->ssrc_reversed = 0;
-                       ri->search_ssrc = FALSE;
-                       add_rtp_notebook(ri);
-                       return;
-               }
-               /* in case we found exactly one matching ssrc for reversed connection */ 
-               case 1: { 
-                       ri->ssrc_reversed = ri->ssrc_tmp[0];
-                       ri->search_ssrc = FALSE;
-                       add_rtp_notebook(ri);
-                       return;
-               }
-               /* there is more then one matching ssrc, so we have to choose between them */
-               default: {
-                       ri->search_ssrc = FALSE;
-                       /* let's draw the window */
-                       label = gtk_label_new("Found more SSRC values for the reversed\n"
-                                                "connection with following parameters:\n");
-                       g_snprintf(temp, 149, "Source %s port %u Destination %s port %u", 
-                                       ri->destination, ri->dstport, ri->source, ri->srcport);
-                       label2 = gtk_label_new(temp);
-                       gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE, FALSE, 0);
-                       gtk_box_pack_start(GTK_BOX(main_vb), label2, FALSE, FALSE, 0);
-                       scroll_r = gtk_scrolled_window_new(NULL, NULL);
-                       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_r), 
-                                               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-                       clist_r = gtk_clist_new(1);
-                       gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
-                       gtk_container_add(GTK_CONTAINER(scroll_r), clist_r);
-                       gtk_box_pack_start(GTK_BOX(main_vb), scroll_r, TRUE, TRUE, 0);
-                       label1 = gtk_label_new("Select one value and click apply");
-                       gtk_box_pack_start(GTK_BOX(main_vb), label1, FALSE, FALSE, 0);
-
-                       main_hbnbox = gtk_hbutton_box_new();
-                       gtk_box_pack_start(GTK_BOX(main_vb), main_hbnbox, FALSE, TRUE, 0);
-                       gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
-                       gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox), 
-                                                               GTK_BUTTONBOX_SPREAD);
-                       gtk_widget_show(main_hbnbox);
-
-                       ok_bt = gtk_button_new_with_label("Apply");
-                       gtk_container_add(GTK_CONTAINER(main_hbnbox), ok_bt);
-                       gtk_signal_connect(GTK_OBJECT(clist_r), "select_row", 
-                                       GTK_SIGNAL_FUNC(get_selected_ssrc), ri);
-                       gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", 
-                                       GTK_SIGNAL_FUNC(apply_selected_ssrc), ri);
-
-                       /* add all the ssrc values in the clist */
-                       /* XXX I'm sure the tmp variable could be avoided here
-                        * i tried to assign guint32 from ri->ssrc_tmp somehow to gchar **text
-                        * but gave up. So if you can do this, just go ahead */
-                       for (i=0; i < ri->reversed_ip_and_port; i++) {
-                               gchar *text[1];
-                               gchar tmp[20];
-                               g_snprintf(tmp, 20, "%u", ri->ssrc_tmp[i]);
-                               text[0] = (gchar *)&tmp;
-                               gtk_clist_append(GTK_CLIST(clist_r), text);
-                       }
-                       
-                       gtk_clist_select_row(GTK_CLIST(clist_r), 0, 0);
-
-                       gtk_widget_show(label);
-                       gtk_widget_show(label1);
-                       gtk_widget_show(label2);
-                       gtk_widget_show(ok_bt);
-                       gtk_widget_show(clist_r);
-                       gtk_widget_show(scroll_r);
-               }
-       }
-}
-
-/* XXX only handles RTP over IPv4, should add IPv6 support */
-/* when the user clicks the RTP dialog button */
-static void rtp_analyse_cb(GtkWidget *w _U_, gpointer data _U_) 
-{ 
-  info_stat *rs;
-  gchar filter_text[256];
-  dfilter_t *sfcode;
-  capture_file *cf;
-  epan_dissect_t *edt;
-  gint err;
-  gboolean frame_matched;
-  frame_data *fdata;
-  GString *error_string;
-  int fd;
-
-  /* There's already a "Display Options" dialog box; reactivate it. */
-  if (rtp_w != NULL) {
-       reactivate_window(rtp_w);
-       return;
-  }
-
-  /* Try to compile the filter. */
-  strcpy(filter_text,"rtp && ip");
-  if (!dfilter_compile(filter_text, &sfcode)) {
-       simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
-       return;
-  }
-  /* we load the current file into cf variable */
-  cf = &cfile;
-  fdata = cf->current_frame;
-
-  /* we are on the selected frame now */
-  if (fdata == NULL)
-       return; /* if we exit here it's an error */
-
-  /* XXX instead of looking for RTP protocol like this, we could do the process_node() staff */
-  /* dissect the current frame */
-  if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err)) {
-       simple_dialog(ESD_TYPE_CRIT, NULL,
-                     file_read_error_message(err), cf->filename);
-       return;
-  }
-  edt = epan_dissect_new(TRUE, FALSE);
-  epan_dissect_prime_dfilter(edt, sfcode);
-  epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
-  frame_matched = dfilter_apply_edt(sfcode, edt);
-
-  /* if it is not an rtp frame, exit */
-  frame_matched = dfilter_apply_edt(sfcode, edt);
-  if (frame_matched != 1) {
-       epan_dissect_free(edt);
-       simple_dialog(ESD_TYPE_CRIT, NULL, "You didn't choose a RTP packet!");
-       return;
-       }
-
-  /* in rs we put all the info */
-  rs=g_malloc(sizeof(info_stat));      
-
-  /* ok, it is a RTP frame, so let's get the ip and port values */
-  rs->srcport = edt->pi.srcport;
-  rs->dstport = edt->pi.destport;
-  strncpy(rs->source, ip_to_str(edt->pi.src.data), 16);  
-  strncpy(rs->destination, ip_to_str(edt->pi.dst.data), 16);  
-
-  /* now we need the SSRC value of the current frame */
-  rs->ssrc_forward = process_tree(edt->tree);
-  if (rs->ssrc_forward == 0) {
-       simple_dialog(ESD_TYPE_CRIT, NULL, "SSRC value couldn't be found!");
-       return;
-  }
-
-  /* now we have all the information about the forwarding connection
-   * we need to go through all the packets and search for reversed connection
-   */
-  rs->search_ssrc = TRUE;
-  rs->ssrc_reversed = 0;       
-  rs->reversed_ip = 0;
-  rs->reversed_ip_and_port = 0;
-  rs->ssrc_tmp = NULL;
-
-  sprintf(filter_text,"rtp && ip && !icmp && (( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ) || ( ip.src==%s && udp.srcport==%d && ip.dst==%s && udp.dstport==%d ))",
-         ip_to_str(edt->pi.src.data),
-         edt->pi.srcport,
-         ip_to_str(edt->pi.dst.data),
-         edt->pi.destport,
-         ip_to_str(edt->pi.dst.data),
-         edt->pi.destport,
-         ip_to_str(edt->pi.src.data),
-         edt->pi.srcport
-         );
-/* XXX compiler warning:passing arg 5 of `register_tap_listener' from incompatible pointer type */
-  error_string = register_tap_listener("rtp", rs, filter_text, rtp_reset, rtp_packet, rtp_draw);
-  if (error_string != NULL) {
-        simple_dialog(ESD_TYPE_WARN, NULL, error_string->str);
-       /* XXX is this enough or do I have to free anything else? */
-       g_string_free(error_string, TRUE);
-       g_free(rs);
-       exit(1);
-  }
-
-  /* let's draw the window */
-  rtp_w = dlg_window_new("Ethereal: RTP Analyse");
-  gtk_window_set_position (GTK_WINDOW (rtp_w), GTK_WIN_POS_CENTER);
-  gtk_signal_connect(GTK_OBJECT(rtp_w), "destroy",
-       GTK_SIGNAL_FUNC(rtp_destroy_cb), rs);
-
-  /* Container for each row of widgets */
-  main_vb = gtk_vbox_new(FALSE, 3);
-  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
-  gtk_widget_show(main_vb);
-
-  /* file names for storing sound data */
-  fd = create_tempfile(f_tempname, sizeof(f_tempname), "ethereal_rtp_fwd");
-  rs->forward.fp = fdopen(fd, "wb");
-  fd = create_tempfile(r_tempname, sizeof(f_tempname), "ethereal_rtp_rev");
-  rs->reversed.fp = fdopen(fd, "wb");
-
-  redissect_packets(cf);
-
-  /* so how many reversed connection we have ? */
-  get_reversed_ssrc(rs);
-
-  /* and finally display this window */
-  gtk_widget_show(rtp_w);
-}
-
-static void
-rtp_analyse_init(char *dummy _U_)
-{
-       rtp_analyse_cb(NULL, NULL);
-}
-
-void
-register_tap_listener_gtkrtp(void)
-{
-       register_ethereal_tap("rtp", rtp_analyse_init);
-}
-
-void
-register_tap_menu_gtkrtp(void)
-{
-       register_tap_menu_item("Statistics/RTP Analysis...", rtp_analyse_cb,
-           NULL, NULL);
-}
-
-
-/* here we save it into a file that user specified */
-/* XXX what about endians here? could go something wrong? */
-static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ void *data)
-{
-       info_stat *rs=(info_stat *)data;
-       int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
-       gint16 f_pd;
-       gint16 r_pd;
-       gchar pd[1];
-       guint32 f_write_silence = 0;
-       guint32 r_write_silence = 0;
-       progdlg_t *progbar;
-       guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
-       gboolean stop_flag = FALSE;
-
-       forw_fd = open(f_tempname, O_RDONLY | O_BINARY);
-       if (forw_fd < 0) 
-               return FALSE;
-       rev_fd = open(r_tempname, O_RDONLY | O_BINARY);
-       if (rev_fd < 0) {
-               close(forw_fd); 
-               return FALSE;
-       }
-
-       /* open file for saving */
-       to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
-       if (to_fd < 0) {
-               close(forw_fd);
-               close(rev_fd);
-               return FALSE;
-       }
-
-       progbar = create_progress_dlg("Saving voice in a file", dest, "Stop", &stop_flag);
-
-       /* First we write the .au header. XXX Hope this is endian independant */
-       /* the magic word 0x2e736e64 == .snd */
-       *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
-       *pd = (unsigned char)0x73; write(to_fd, pd, 1);
-       *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
-       *pd = (unsigned char)0x64; write(to_fd, pd, 1);
-       /* header offset == 24 bytes */
-       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       *pd = (unsigned char)0x18; write(to_fd, pd, 1);
-       /* total length, it is permited to set this to 0xffffffff */
-       *pd = (unsigned char)0xff; write(to_fd, pd, 1); 
-       write(to_fd, pd, 1); 
-       write(to_fd, pd, 1); 
-       write(to_fd, pd, 1);
-       /* encoding format == 8 bit ulaw */
-       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       *pd = (unsigned char)0x01; write(to_fd, pd, 1);
-       /* sample rate == 8000 Hz */
-       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
-       *pd = (unsigned char)0x40; write(to_fd, pd, 1);
-       /* channels == 1 */
-       *pd = (unsigned char)0x00; write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       write(to_fd, pd, 1);
-       *pd = (unsigned char)0x01; write(to_fd, pd, 1);
-       
-       switch (channels) {
-               /* only forward direction */
-               case 1: {
-                       progbar_count = rs->forward.count;
-                       progbar_quantum = rs->forward.count/100;
-                       while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
-                               if(stop_flag) 
-                                       break;
-                               if((count > progbar_nextstep) && (count <= progbar_count)) {
-                                       update_progress_dlg(progbar, 
-                                               (gfloat) count/progbar_count, "Saving");
-                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
-                               }
-                               count++;
-                               *pd = (unsigned char)linear2ulaw(f_pd);
-                               fwritten = write(to_fd, pd, 1);
-                               if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
-                                       close(forw_fd);
-                                       close(rev_fd);
-                                       close(to_fd);
-                                       destroy_progress_dlg(progbar);
-                                       return FALSE;
-                               }
-                       }
-                       break;
-               }
-               /* only reversed direction */
-               case 2: {
-                       progbar_count = rs->reversed.count;
-                       progbar_quantum = rs->reversed.count/100;
-                       while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
-                               if(stop_flag) 
-                                       break;
-                               if((count > progbar_nextstep) && (count <= progbar_count)) {
-                                       update_progress_dlg(progbar, 
-                                               (gfloat) count/progbar_count, "Saving");
-                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
-                               }
-                               count++;
-                               *pd = (unsigned char)linear2ulaw(r_pd);
-                               rwritten = write(to_fd, pd, 1);
-                               if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
-                                       close(forw_fd);
-                                       close(rev_fd);
-                                       close(to_fd);
-                                       destroy_progress_dlg(progbar);
-                                       return FALSE;
-                               }
-                       }
-                       break;
-               }
-               /* both directions */
-               default: {
-                       (rs->forward.count > rs->reversed.count) ? 
-                                       (progbar_count = rs->forward.count) : 
-                                               (progbar_count = rs->reversed.count);
-                       progbar_quantum = progbar_count/100;
-                       /* since conversation in one way can start later than in the other one, 
-                        * we have to write some silence information for one channel */
-                       if (rs->forward.start_time > rs->reversed.start_time) {
-                               f_write_silence = 
-                                       (rs->forward.start_time-rs->reversed.start_time)*8000;
-                       }
-                       else if (rs->forward.start_time < rs->reversed.start_time) {
-                               r_write_silence = 
-                                       (rs->reversed.start_time-rs->forward.start_time)*8000;
-                       }
-                       for(;;) {
-                               if(stop_flag) 
-                                       break;
-                               if((count > progbar_nextstep) && (count <= progbar_count)) {
-                                       update_progress_dlg(progbar, 
-                                               (gfloat) count/progbar_count, "Saving");
-                                       progbar_nextstep = progbar_nextstep + progbar_quantum;
-                               }
-                               count++;
-                               if(f_write_silence > 0) {
-                                       rread = read(rev_fd, &r_pd, 2);
-                                       f_pd = 0;
-                                       fread = 1;
-                                       f_write_silence--;
-                               }
-                               else if(r_write_silence > 0) {
-                                       fread = read(forw_fd, &f_pd, 2);
-                                       r_pd = 0;
-                                       rread = 1;
-                                       r_write_silence--;
-                               }
-                               else {
-                                       fread = read(forw_fd, &f_pd, 2); 
-                                       rread = read(rev_fd, &r_pd, 2);
-                               }
-                               if ((rread == 0) && (fread == 0)) 
-                                       break;
-                               *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
-                               rwritten = write(to_fd, pd, 1);
-                               if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
-                                       close(forw_fd);
-                                       close(rev_fd);
-                                       close(to_fd);
-                                       destroy_progress_dlg(progbar);
-                                       return FALSE;
-                               }
-                       }
-               }
-       }
-       destroy_progress_dlg(progbar);
-       close(forw_fd);
-       close(rev_fd);
-       close(to_fd);
-       return TRUE;
-
-}