1d85416720fc154fc5dfa2e20821f68745d30280
[obnox/wireshark/wip.git] / gtk / rtp_analysis.c
1 /* rtp_analysis.c
2  * RTP analysis addition for ethereal
3  *
4  * $Id: rtp_analysis.c,v 1.33 2004/02/11 01:23:25 guy Exp $
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * based on tap_rtp.c
10  * Copyright 2003, Iskratel, Ltd, Kranj
11  * By Miha Jemec <m.jemec@iskratel.si>
12  *
13  * Ethereal - Network traffic analyzer
14  * By Gerald Combs <gerald@ethereal.com>
15  * Copyright 1998 Gerald Combs
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * as published by the Free Software Foundation; either version 2
20  * of the License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 /*do not define this symbol. will be added soon*/
37 /*#define USE_CONVERSATION_GRAPH 1*/
38
39 #include "rtp_analysis.h"
40 #include "rtp_stream.h"
41 #include "rtp_stream_dlg.h"
42
43 #ifdef USE_CONVERSATION_GRAPH
44 #include "../graph/graph.h"
45 #endif
46
47 #include "util.h"
48 #include "epan/epan_dissect.h"
49 #include "epan/filesystem.h"
50 #include "tap.h"
51 #include "register.h"
52 #include "packet-rtp.h"
53 #include "g711.h"
54 #include "rtp_pt.h"
55
56 #ifdef NEED_MKSTEMP
57 #include "mkstemp.h"
58 #endif
59
60 /* in /gtk ... */
61 #include "dlg_utils.h"
62 #include "ui_util.h"
63 #include "alert_box.h"
64 #include "simple_dialog.h"
65 #include "menu.h"
66 #include "main.h"
67 #include "progress_dlg.h"
68 #include "compat_macros.h"
69
70 #include "image/clist_ascend.xpm"
71 #include "image/clist_descend.xpm"
72
73 #include <math.h>
74 #include <fcntl.h>
75 #include <string.h>
76
77 #ifdef HAVE_UNISTD_H
78 #include <unistd.h>
79 #endif
80
81 #ifdef HAVE_IO_H
82 #include <io.h> /* open/close on win32 */
83 #endif
84
85 #ifndef O_BINARY
86 #define O_BINARY 0
87 #endif
88
89 /****************************************************************************/
90
91 typedef struct column_arrows {
92         GtkWidget *table;
93         GtkWidget *ascend_pm;
94         GtkWidget *descend_pm;
95 } column_arrows;
96
97 typedef struct _dialog_data_t {
98         GtkWidget *window;
99         GtkCList *clist_fwd;
100         GtkCList *clist_rev;
101         GtkWidget *label_stats_fwd;
102         GtkWidget *label_stats_rev;
103         column_arrows *col_arrows_fwd;
104         column_arrows *col_arrows_rev;
105         GtkWidget *notebook;
106         GtkCList *selected_clist;
107         GtkWidget *save_voice_as_w;
108         GtkWidget *save_csv_as_w;
109         gint selected_row;
110 #ifdef USE_CONVERSATION_GRAPH
111         GtkWidget *graph_window;
112 #endif
113 } dialog_data_t;
114
115 #define OK_TEXT "[ Ok ]"
116 #define PT_UNDEFINED -1
117
118 /* type of error when saving voice in a file didn't succeed */
119 typedef enum {
120         TAP_RTP_WRONG_CODEC,
121         TAP_RTP_WRONG_LENGTH,
122         TAP_RTP_PADDING_ERROR,
123         TAP_RTP_SHORT_FRAME,
124         TAP_RTP_FILE_OPEN_ERROR,
125         TAP_RTP_NO_DATA
126 } error_type_t; 
127
128
129 /****************************************************************************/
130 /* structure that holds the information about the forward and reversed direction */
131 typedef struct _tap_rtp_stat_t {
132         gboolean first_packet;     /* do not use in code that is called after rtp_packet_analyse */
133                                    /* use (flags & STAT_FLAG_FIRST) instead */
134         /* all of the following fields will be initialized after
135          rtp_packet_analyse has been called */
136         guint32 flags;             /* see STAT_FLAG-defines below */
137         guint16 seq_num;
138         guint32 timestamp;
139         guint32 delta_timestamp;
140         double delay;
141         double jitter;
142         double time;
143         double start_time;
144         double max_delay;
145         guint32 max_nr;
146         guint16 start_seq_nr;
147         guint16 stop_seq_nr;
148         guint32 total_nr;
149         guint32 sequence;
150         gboolean under;
151         gint cycles;
152         guint16 pt;
153         int reg_pt;
154 } tap_rtp_stat_t;
155
156 /* status flags for the flags parameter in tap_rtp_stat_t */
157 #define STAT_FLAG_FIRST       0x01
158 #define STAT_FLAG_MARKER      0x02
159 #define STAT_FLAG_WRONG_SEQ   0x04
160 #define STAT_FLAG_PT_CHANGE   0x08
161 #define STAT_FLAG_PT_CN       0x10
162 #define STAT_FLAG_FOLLOW_PT_CN  0x20
163 #define STAT_FLAG_REG_PT_CHANGE  0x40
164
165 typedef struct _tap_rtp_save_info_t {
166         FILE *fp;
167         guint32 count;
168         error_type_t error_type;
169         gboolean saved;
170 } tap_rtp_save_info_t;
171
172
173 /* structure that holds the information about the forward and reversed direction */
174 struct _info_direction {
175         tap_rtp_stat_t statinfo;
176         tap_rtp_save_info_t saveinfo;
177 };
178
179 #define TMPNAMSIZE 100
180
181 /* structure that holds general information about the connection 
182 * and structures for both directions */
183 typedef struct _user_data_t {
184         /* tap associated data*/
185         guint32 ip_src_fwd;
186         guint16 port_src_fwd;
187         guint32 ip_dst_fwd;
188         guint16 port_dst_fwd;
189         guint32 ssrc_fwd;
190         guint32 ip_src_rev;
191         guint16 port_src_rev;
192         guint32 ip_dst_rev;
193         guint16 port_dst_rev;
194         guint32 ssrc_rev;
195
196         struct _info_direction forward;
197         struct _info_direction reversed;
198
199         char f_tempname[TMPNAMSIZE];
200         char r_tempname[TMPNAMSIZE];
201
202         /* dialog associated data */
203         dialog_data_t dlg;
204
205 #ifdef USE_CONVERSATION_GRAPH
206         time_series_t series_fwd;
207         time_series_t series_rev;
208 #endif
209 } user_data_t;
210
211
212 /* Column titles. */
213 static gchar *titles[8] =  {
214         "Packet",
215         "Sequence",
216         "Delay (s)",
217         "Jitter (s)",
218         "Marker",
219         "Status",
220         "Date",
221         "Length"
222 };
223
224
225 typedef const guint8 * ip_addr_p;
226
227
228 /****************************************************************************/
229 /* TAP FUNCTIONS */
230
231 /****************************************************************************/
232 /* when there is a [re]reading of packet's */
233 static void
234 rtp_reset(void *user_data_arg)
235 {
236         user_data_t *user_data = user_data_arg;
237
238         user_data->forward.statinfo.first_packet = TRUE;
239         user_data->reversed.statinfo.first_packet = TRUE;
240         user_data->forward.statinfo.max_delay = 0;
241         user_data->reversed.statinfo.max_delay = 0;
242         user_data->forward.statinfo.delay = 0;
243         user_data->reversed.statinfo.delay = 0;
244         user_data->forward.statinfo.jitter = 0;
245         user_data->reversed.statinfo.jitter = 0;
246         user_data->forward.statinfo.timestamp = 0;
247         user_data->reversed.statinfo.timestamp = 0;
248         user_data->forward.statinfo.max_nr = 0;
249         user_data->reversed.statinfo.max_nr = 0;
250         user_data->forward.statinfo.total_nr = 0;
251         user_data->reversed.statinfo.total_nr = 0;
252         user_data->forward.statinfo.sequence = 0;
253         user_data->reversed.statinfo.sequence = 0;
254         user_data->forward.statinfo.start_seq_nr = 0;
255         user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
256         user_data->forward.statinfo.stop_seq_nr = 0;
257         user_data->reversed.statinfo.stop_seq_nr = 0;
258         user_data->forward.statinfo.cycles = 0;
259         user_data->reversed.statinfo.cycles = 0;
260         user_data->forward.statinfo.under = FALSE;
261         user_data->reversed.statinfo.under = FALSE;
262         user_data->forward.statinfo.start_time = 0;
263         user_data->reversed.statinfo.start_time = 0;
264         user_data->forward.statinfo.time = 0;
265         user_data->reversed.statinfo.time = 0;
266         user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
267         user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
268
269         user_data->forward.saveinfo.count = 0;
270         user_data->reversed.saveinfo.count = 0;
271         user_data->forward.saveinfo.saved = FALSE;
272         user_data->reversed.saveinfo.saved = FALSE;
273
274 #ifdef USE_CONVERSATION_GRAPH
275         if (user_data->dlg.graph_window != NULL)
276                 gtk_widget_destroy(user_data->dlg.graph_window);
277         
278         g_array_free(user_data->series_fwd.value_pairs, TRUE);
279         user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
280
281         g_array_free(user_data->series_rev.value_pairs, TRUE);
282         user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
283 #endif
284
285         /* XXX check for error at fclose? */
286         if (user_data->forward.saveinfo.fp != NULL)
287                 fclose(user_data->forward.saveinfo.fp); 
288         if (user_data->reversed.saveinfo.fp != NULL)
289                 fclose(user_data->reversed.saveinfo.fp); 
290         user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb"); 
291         if (user_data->forward.saveinfo.fp == NULL)
292                 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
293         user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
294         if (user_data->reversed.saveinfo.fp == NULL)
295                 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
296         return;
297 }
298
299 /****************************************************************************/
300 /* here we can redraw the output */
301 /* not used yet */
302 static void rtp_draw(void *prs _U_)
303 {
304         return;
305 }
306
307 /* forward declarations */
308 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
309                          double delay, double jitter, gchar *status, gboolean marker,
310                          gchar *timeStr, guint32 pkt_len, GdkColor *color);
311
312 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
313                                                           packet_info *pinfo, struct _rtp_info *rtpinfo);
314 static int rtp_packet_add_info(GtkCList *clist,
315         tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
316 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo, 
317                                                                    tap_rtp_stat_t *statinfo,
318                                                                    packet_info *pinfo, struct _rtp_info *rtpinfo);
319
320
321 /****************************************************************************/
322 /* whenever a RTP packet is seen by the tap listener */
323 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *rtpinfo_arg)
324 {
325         user_data_t *user_data = user_data_arg;
326         struct _rtp_info *rtpinfo = rtpinfo_arg;
327 #ifdef USE_CONVERSATION_GRAPH
328         value_pair_t vp;
329 #endif
330
331         /* we ignore packets that are not displayed */
332         if (pinfo->fd->flags.passed_dfilter == 0)
333                 return 0;
334
335         /* is it the forward direction?  */
336         else if (user_data->ssrc_fwd == rtpinfo->info_sync_src)  {
337 #ifdef USE_CONVERSATION_GRAPH
338                 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
339                 vp.fnumber = pinfo->fd->num;
340                 g_array_append_val(user_data->series_fwd.value_pairs, vp);
341 #endif
342                 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
343                 rtp_packet_add_info(user_data->dlg.clist_fwd,
344                         &(user_data->forward.statinfo), pinfo, rtpinfo);
345                 rtp_packet_save_payload(&(user_data->forward.saveinfo),
346                         &(user_data->forward.statinfo), pinfo, rtpinfo);
347         }
348         /* is it the reversed direction? */
349         else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
350 #ifdef USE_CONVERSATION_GRAPH
351                 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
352                 vp.fnumber = pinfo->fd->num;
353                 g_array_append_val(user_data->series_rev.value_pairs, vp);
354 #endif
355                 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
356                 rtp_packet_add_info(user_data->dlg.clist_rev,
357                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
358                 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
359                         &(user_data->reversed.statinfo), pinfo, rtpinfo);
360         }
361
362         return 0;
363 }
364
365
366 /****************************************************************************/
367 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
368                                                           packet_info *pinfo, struct _rtp_info *rtpinfo)
369 {
370         double current_time;
371         double current_jitter;
372
373         statinfo->flags = 0;
374
375         /* check payload type */
376         if (rtpinfo->info_payload_type == PT_CN
377                 || rtpinfo->info_payload_type == PT_CN_OLD)
378                 statinfo->flags |= STAT_FLAG_PT_CN;
379         if (statinfo->pt == PT_CN
380                 || statinfo->pt == PT_CN_OLD)
381                 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
382         if (rtpinfo->info_payload_type != statinfo->pt)
383                 statinfo->flags |= STAT_FLAG_PT_CHANGE;
384
385         statinfo->pt = rtpinfo->info_payload_type;
386
387         /* store the current time and calculate the current jitter */
388         current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
389         current_jitter = statinfo->jitter + ( fabs (current_time - (statinfo->time) -
390                 ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/8000)- statinfo->jitter)/16;
391         statinfo->delay = current_time-(statinfo->time);
392         statinfo->jitter = current_jitter;
393
394         /*  is this the first packet we got in this direction? */
395         if (statinfo->first_packet) {
396                 statinfo->start_seq_nr = rtpinfo->info_seq_num;
397                 statinfo->start_time = current_time;
398                 statinfo->delay = 0;
399                 statinfo->jitter = 0;
400                 statinfo->flags |= STAT_FLAG_FIRST;
401                 statinfo->first_packet = FALSE;
402         }
403         /* is it a packet with the mark bit set? */
404         if (rtpinfo->info_marker_set) {
405                 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
406                 statinfo->flags |= STAT_FLAG_MARKER;
407         }
408         /* is it a regular packet? */
409         if (!(statinfo->flags & STAT_FLAG_FIRST)
410                 && !(statinfo->flags & STAT_FLAG_MARKER)
411                 && !(statinfo->flags & STAT_FLAG_PT_CN)
412                 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
413                 /* include it in maximum delay calculation */
414                 if (statinfo->delay > statinfo->max_delay) {
415                         statinfo->max_delay = statinfo->delay;
416                         statinfo->max_nr = pinfo->fd->num;
417                 }
418         }
419         /* regular payload change? (CN ignored) */
420         if (!(statinfo->flags & STAT_FLAG_FIRST)
421                 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
422                 if ((statinfo->pt != statinfo->reg_pt)
423                         && (statinfo->reg_pt != PT_UNDEFINED)) {
424                         statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
425                 }
426         }
427
428         /* set regular payload*/
429         if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
430                 statinfo->reg_pt = statinfo->pt;
431         }
432
433
434         /* When calculating expected rtp packets the seq number can wrap around
435         * so we have to count the number of cycles
436         * Variable cycles counts the wraps around in forwarding connection and
437         * under is flag that indicates where we are
438         *
439         * XXX how to determine number of cycles with all possible lost, late
440         * and duplicated packets without any doubt? It seems to me, that
441         * because of all possible combination of late, duplicated or lost
442         * packets, this can only be more or less good approximation
443         *
444         * There are some combinations (rare but theoretically possible),
445         * where below code won't work correctly - statistic may be wrong then.
446         */
447
448         /* so if the current sequence number is less than the start one
449         * we assume, that there is another cycle running */
450         if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
451                 statinfo->cycles++;
452                 statinfo->under = TRUE;
453         }
454         /* what if the start seq nr was 0? Then the above condition will never
455         * be true, so we add another condition. XXX The problem would arise
456         * if one of the packets with seq nr 0 or 65535 would be lost or late */
457         else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
458                 (statinfo->under == FALSE)){
459                 statinfo->cycles++;
460                 statinfo->under = TRUE;
461         }
462         /* the whole round is over, so reset the flag */
463         else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
464                 statinfo->under = FALSE;
465         }
466
467         /* Since it is difficult to count lost, duplicate or late packets separately,
468         * we would like to know at least how many times the sequence number was not ok */
469
470         /* if the current seq number equals the last one or if we are here for
471         * the first time, then it is ok, we just store the current one as the last one */
472         if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
473                 statinfo->seq_num = rtpinfo->info_seq_num;
474         /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
475         else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
476                 statinfo->seq_num = rtpinfo->info_seq_num;
477         /* lost packets */
478         else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
479                 statinfo->seq_num = rtpinfo->info_seq_num;
480                 statinfo->sequence++;
481                 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
482         }
483         /* late or duplicated */
484         else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
485                 statinfo->sequence++;
486                 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
487         }
488
489         statinfo->time = current_time;
490         statinfo->timestamp = rtpinfo->info_timestamp;
491         statinfo->stop_seq_nr = rtpinfo->info_seq_num;
492         statinfo->total_nr++;
493
494         return 0;
495 }
496
497
498 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
499 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
500 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
501 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
502
503 /****************************************************************************/
504 /* adds statistics information from the packet to the clist */
505 static int rtp_packet_add_info(GtkCList *clist,
506         tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
507 {
508         guint16 msecs;
509         gchar timeStr[32];
510         struct tm *tm_tmp;
511         time_t then;
512         gchar status[40];
513         GdkColor color = COLOR_DEFAULT;
514
515         then = pinfo->fd->abs_secs;
516         msecs = (guint16)(pinfo->fd->abs_usecs/1000);
517         tm_tmp = localtime(&then);
518         snprintf(timeStr,32,"%02d/%02d/%04d %02d:%02d:%02d.%03d",
519                 tm_tmp->tm_mon + 1,
520                 tm_tmp->tm_mday,
521                 tm_tmp->tm_year + 1900,
522                 tm_tmp->tm_hour,
523                 tm_tmp->tm_min,
524                 tm_tmp->tm_sec,
525                 msecs);
526
527         if (statinfo->pt == PT_CN) {
528                 snprintf(status,40,"Comfort noise (PT=13, RFC 3389)");
529                 color = COLOR_CN;
530         }
531         else if (statinfo->pt == PT_CN_OLD) {
532                 snprintf(status,40,"Comfort noise (PT=19, reserved)");
533                 color = COLOR_CN;
534         }
535         else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
536                 snprintf(status,40,"Wrong sequence nr.");
537                 color = COLOR_ERROR;
538         }
539         else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
540                 snprintf(status,40,"Payload changed to PT=%u", statinfo->pt);
541                 color = COLOR_WARNING;
542         }
543         else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
544                 &&  !(statinfo->flags & STAT_FLAG_FIRST)
545                 &&  !(statinfo->flags & STAT_FLAG_PT_CN)
546                 &&  (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
547                 &&  !(statinfo->flags & STAT_FLAG_MARKER)) {
548                 snprintf(status,40,"Marker missing?");
549                 color = COLOR_WARNING;
550         }
551         else {
552                 if (statinfo->flags & STAT_FLAG_MARKER) {
553                         color = COLOR_WARNING;
554                 }
555                 snprintf(status,40,OK_TEXT);
556         }
557
558         /*  is this the first packet we got in this direction? */
559         if (statinfo->flags & STAT_FLAG_FIRST) {
560                 add_to_clist(clist,
561                         pinfo->fd->num, rtpinfo->info_seq_num,
562                         0,
563                         0,
564                         status,
565                         rtpinfo->info_marker_set,
566                         timeStr, pinfo->fd->pkt_len,
567                         &color);
568         }
569         else {
570                 add_to_clist(clist,
571                         pinfo->fd->num, rtpinfo->info_seq_num,
572                         statinfo->delay,
573                         statinfo->jitter,
574                         status,
575                         rtpinfo->info_marker_set,
576                         timeStr, pinfo->fd->pkt_len,
577                         &color);
578         }
579
580         return 0;
581 }
582
583
584 /****************************************************************************/
585 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo, 
586                                                                    tap_rtp_stat_t *statinfo,
587                                                                    packet_info *pinfo, struct _rtp_info *rtpinfo)
588 {
589         guint i;
590         const guint8 *data;
591         gint16 tmp;
592
593         /*  is this the first packet we got in this direction? */
594         if (statinfo->flags & STAT_FLAG_FIRST) {
595                 if (saveinfo->fp == NULL) {
596                         saveinfo->saved = FALSE;
597                         saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
598                 }
599                 else
600                         saveinfo->saved = TRUE;
601         }
602
603         /* save the voice information */
604         /* if there was already an error, we quit */
605         if (saveinfo->saved == FALSE)
606                 return 0;
607
608         /* if the captured length and packet length aren't equal, we quit
609         * because there is some information missing */
610         if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
611                 saveinfo->saved = FALSE;
612                 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
613                 return 0;
614         }
615
616         /* if padding bit is set, but the padding count is bigger
617         * then the whole RTP data - error with padding count */
618         if ( (rtpinfo->info_padding_set != FALSE) &&
619                 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
620                 saveinfo->saved = FALSE;
621                 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
622                 return 0;
623         }
624
625         /* do we need to insert some silence? */
626         if ((rtpinfo->info_marker_set) &&
627                 !(statinfo->flags & STAT_FLAG_FIRST) &&
628                 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) )  {
629                 /* the amount of silence should be the difference between
630                 * the last timestamp and the current one minus x
631                 * x should equal the amount of information in the last frame
632                 * XXX not done yet */
633                 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
634                         rtpinfo->info_padding_count); i++) {
635                         tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
636                         fwrite(&tmp, 2, 1, saveinfo->fp);
637                         saveinfo->count++;
638                 }
639                 fflush(saveinfo->fp);
640         }
641
642         /* ulaw? */
643         if (rtpinfo->info_payload_type == PT_PCMU) {
644                 if (!rtpinfo->info_all_data_present) {
645                         /* Not all the data was captured. */
646                         saveinfo->saved = FALSE;
647                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
648                         return 0;
649                 }
650
651                 /* we put the pointer at the beginning of the RTP
652                 * payload, that is, at the beginning of the RTP data
653                 * plus the offset of the payload from the beginning
654                 * of the RTP data */
655                 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
656                 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
657                         tmp = (gint16 )ulaw2linear((unsigned char)*data);
658                         fwrite(&tmp, 2, 1, saveinfo->fp);
659                         saveinfo->count++;
660                 }
661                 fflush(saveinfo->fp);
662                 saveinfo->saved = TRUE;
663                 return 0;
664         }
665
666         /* alaw? */
667         else if (rtpinfo->info_payload_type == PT_PCMA) {
668                 if (!rtpinfo->info_all_data_present) {
669                         /* Not all the data was captured. */
670                         saveinfo->saved = FALSE;
671                         saveinfo->error_type = TAP_RTP_SHORT_FRAME;
672                         return 0;
673                 }
674
675                 /* we put the pointer at the beginning of the RTP
676                 * payload, that is, at the beginning of the RTP data
677                 * plus the offset of the payload from the beginning
678                 * of the RTP data */
679                 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
680                 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
681                         tmp = (gint16 )alaw2linear((unsigned char)*data);
682                         fwrite(&tmp, 2, 1, saveinfo->fp);
683                         saveinfo->count++;
684                 }
685                 fflush(saveinfo->fp);
686                 saveinfo->saved = TRUE;
687                 return 0;
688         }
689         /* comfort noise? - do nothing */
690         else if (rtpinfo->info_payload_type == PT_CN
691                 || rtpinfo->info_payload_type == PT_CN_OLD) {
692         }
693         /* unsupported codec or XXX other error */
694         else {
695                 saveinfo->saved = FALSE;
696                 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
697                 return 0;
698         }
699
700         return 0;
701 }
702
703
704 /****************************************************************************/
705 /* CALLBACKS */
706
707 /****************************************************************************/
708 /* XXX just copied from gtk/rpc_stat.c */
709 void protect_thread_critical_region(void);
710 void unprotect_thread_critical_region(void);
711
712
713 /****************************************************************************/
714 /* close the dialog window and remove the tap listener */
715 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
716 {
717         /* close and remove temporary files */
718         if (user_data->forward.saveinfo.fp != NULL)
719                 fclose(user_data->forward.saveinfo.fp);
720         if (user_data->reversed.saveinfo.fp != NULL)
721                 fclose(user_data->reversed.saveinfo.fp);
722         /*XXX: test for error **/
723         remove(user_data->f_tempname);
724         remove(user_data->r_tempname);
725
726         /* destroy save_voice_as window if open */
727         if (user_data->dlg.save_voice_as_w != NULL)
728                 gtk_widget_destroy(user_data->dlg.save_voice_as_w);
729
730 #ifdef USE_CONVERSATION_GRAPH
731         /* destroy graph window if open */
732         if (user_data->dlg.graph_window != NULL)
733                 gtk_widget_destroy(user_data->dlg.graph_window);
734 #endif
735
736         g_free(user_data->dlg.col_arrows_fwd);
737         g_free(user_data->dlg.col_arrows_rev);
738         g_free(user_data);
739 }
740
741
742 /****************************************************************************/
743 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
744                                     GtkNotebookPage *page _U_,
745                                     gint page_num _U_,
746                                     user_data_t *user_data _U_)
747 {
748         user_data->dlg.selected_clist =
749                 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
750         user_data->dlg.selected_row = 0;
751 }
752
753 /****************************************************************************/
754 static void on_clist_select_row(GtkCList        *clist _U_,
755                                 gint             row _U_,
756                                 gint             column _U_,
757                                 GdkEvent        *event _U_,
758                                 user_data_t     *user_data _U_)
759 {
760         user_data->dlg.selected_clist = clist;
761         user_data->dlg.selected_row = row;
762 }
763
764
765 #ifdef USE_CONVERSATION_GRAPH
766 /****************************************************************************/
767 /* when the graph window gets destroyed */
768 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
769 {
770         /* note that graph window has been destroyed */
771         user_data->dlg.graph_window = NULL;
772 }
773
774 /****************************************************************************/
775 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
776 {
777         guint row;
778         GtkCList *clist = NULL;
779         if (vp.fnumber != 0) {
780                 clist = GTK_CLIST(user_data->dlg.clist_fwd);
781                 row = gtk_clist_find_row_from_data(clist,
782                                 GUINT_TO_POINTER(vp.fnumber));
783                 if (row==-1) {
784                         clist = GTK_CLIST(user_data->dlg.clist_rev);
785                         row = gtk_clist_find_row_from_data(clist,
786                                         GUINT_TO_POINTER(vp.fnumber));
787                 }
788                 if (row!=-1) {
789                         gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
790                                 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
791                         gtk_clist_select_row(clist, row, 0);
792                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
793                 }
794         }
795 }
796
797
798 /****************************************************************************/
799 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
800 {
801         gchar title1[80];
802         gchar title2[80];
803         GList *list = NULL;
804         
805         if (user_data->dlg.graph_window != NULL) {
806                 /* There's already a graph window; reactivate it. */
807                 reactivate_window(user_data->dlg.graph_window);
808                 return;
809         }
810                 
811         list = g_list_append(list, &(user_data->series_fwd));
812         list = g_list_append(list, &(user_data->series_rev));
813
814         user_data->series_fwd.color.pixel = 0;
815         user_data->series_fwd.color.red = 0x80ff;
816         user_data->series_fwd.color.green = 0xe0ff;
817         user_data->series_fwd.color.blue = 0xffff;
818         user_data->series_fwd.yvalue = 0.5;
819
820         user_data->series_rev.color.pixel = 0;
821         user_data->series_rev.color.red = 0x60ff;
822         user_data->series_rev.color.green = 0xc0ff;
823         user_data->series_rev.color.blue = 0xffff;
824         user_data->series_rev.yvalue = -0.5;
825
826         g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
827                 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
828                 user_data->port_src_fwd,
829                 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
830                 user_data->port_dst_fwd,
831                 user_data->ssrc_fwd);
832
833         g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
834                 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
835                 user_data->port_src_rev,
836                 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
837                 user_data->port_dst_rev,
838                 user_data->ssrc_rev);
839
840         user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
841                 &graph_selection_callback, user_data);
842         SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
843                        on_destroy_graph, user_data);
844 }
845 #endif /*USE_CONVERSATION_GRAPH*/
846
847
848 /****************************************************************************/
849 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
850 {
851         guint fnumber;
852
853         if (user_data->dlg.selected_clist!=NULL) {
854                 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
855                         GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
856                 goto_frame(&cfile, fnumber);
857         }
858 }
859
860
861 static void draw_stat(user_data_t *user_data);
862
863 /****************************************************************************/
864 /* re-dissects all packets */
865 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
866 {
867         gchar filter_text[256];
868         dfilter_t *sfcode;
869         GString *error_string;
870
871         /* clear the dialog box clists */
872         gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
873         gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
874
875         /* try to compile the filter. */
876         strcpy(filter_text,"rtp && ip");
877         if (!dfilter_compile(filter_text, &sfcode)) {
878                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK, dfilter_error_msg);
879                 return;
880         }
881
882         sprintf(filter_text,"rtp && (( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ) || ( ip.src==%s && udp.srcport==%u && ip.dst==%s && udp.dstport==%u ))",
883                 ip_to_str((ip_addr_p)&(user_data->ip_src_fwd)),
884                 user_data->port_src_fwd,
885                 ip_to_str((ip_addr_p)&(user_data->ip_dst_fwd)),
886                 user_data->port_dst_fwd,
887                 ip_to_str((ip_addr_p)&(user_data->ip_src_rev)),
888                 user_data->port_src_rev,
889                 ip_to_str((ip_addr_p)&(user_data->ip_dst_rev)),
890                 user_data->port_dst_rev
891                 );
892
893         /* register tap listener */
894         error_string = register_tap_listener("rtp", user_data, filter_text,
895                 rtp_reset, rtp_packet, rtp_draw);
896         if (error_string != NULL) {
897                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK, error_string->str);
898                         g_string_free(error_string, TRUE);
899                 return;
900         }
901
902         /* retap all packets */
903         retap_packets(&cfile);
904
905         /* remove tap listener again */
906         protect_thread_critical_region();
907         remove_tap_listener(user_data);
908         unprotect_thread_critical_region();
909
910         /* draw statistics info */
911         draw_stat(user_data);
912
913         gtk_clist_sort(user_data->dlg.clist_fwd);
914         gtk_clist_sort(user_data->dlg.clist_rev);
915 }
916
917 /****************************************************************************/
918 /* on_destroy is automatically called after that */
919 static void on_close_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
920 {
921         gtk_grab_remove(GTK_WIDGET(user_data->dlg.window));
922         gtk_widget_destroy(GTK_WIDGET(user_data->dlg.window));
923 }
924
925 /****************************************************************************/
926 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
927 {
928         GtkCList *clist;
929         gchar *text;
930         gint row;
931         if (user_data->dlg.selected_clist==NULL)
932                 return;
933
934         clist = user_data->dlg.selected_clist;
935         row = user_data->dlg.selected_row + 1;
936
937         while (gtk_clist_get_text(clist,row,5,&text)) {
938                 if (strcmp(text, OK_TEXT) != 0) {
939                         gtk_clist_select_row(clist, row, 0);
940                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
941                         return;
942                 }
943                 ++row;
944         }
945
946         /* wrap around */
947         row = 0;
948         while (gtk_clist_get_text(clist,row,5,&text) && row<user_data->dlg.selected_row) {
949                 if (strcmp(text, OK_TEXT) != 0) {
950                         gtk_clist_select_row(clist, row, 0);
951                         gtk_clist_moveto(clist, row, 0, 0.5, 0);
952                         return;
953                 }
954                 ++row;
955         }
956 }
957
958 /****************************************************************************/
959 /* when we want to save the information */
960 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
961 {
962         gchar *g_dest;
963         GtkWidget *rev, *forw, *both;
964         user_data_t *user_data;
965         
966         FILE *fp;
967         char *columnText;
968         int i,j;
969         
970         g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
971         
972         /* Perhaps the user specified a directory instead of a file.
973         Check whether they did. */
974         if (test_for_directory(g_dest) == EISDIR) {
975                 /* It's a directory - set the file selection box to display it. */
976                 set_last_open_dir(g_dest);
977                 g_free(g_dest);
978                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
979                 return;
980         }
981         
982         rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
983         forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
984         both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
985         user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
986         
987         if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
988                 fp = fopen(g_dest, "w");
989                 if (fp == NULL) {
990                         open_failure_alert_box(g_dest, errno, TRUE);
991                         return;
992                 }
993                 
994                 if (GTK_TOGGLE_BUTTON(both)->active) {
995                         fprintf(fp, "Forward\n");
996                         if (ferror(fp)) {
997                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
998                                     file_write_error_message(errno), g_dest);
999                                 fclose(fp);
1000                                 return;
1001                         }
1002                 }
1003                 
1004                 for(j = 0; j < 8; j++) {
1005                         if (j == 0) {
1006                                 fprintf(fp,"%s",titles[j]);
1007                         } else {
1008                                 fprintf(fp,",%s",titles[j]);
1009                         }
1010                 }
1011                 fprintf(fp,"\n");
1012                 if (ferror(fp)) {
1013                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1014                             file_write_error_message(errno), g_dest);
1015                         fclose(fp);
1016                         return;
1017                 }
1018                 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
1019                         for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1020                                 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1021                                 if (j == 0) {
1022                                         fprintf(fp,"%s",columnText);
1023                                 } else {
1024                                         fprintf(fp,",%s",columnText);
1025                                 }
1026                         }
1027                         fprintf(fp,"\n");
1028                         if (ferror(fp)) {
1029                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1030                                     file_write_error_message(errno), g_dest);
1031                                 fclose(fp);
1032                                 return;
1033                         }
1034                 }
1035                 
1036                 if (fclose(fp) == EOF) {
1037                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1038                             file_write_error_message(errno), g_dest);
1039                         return;
1040                 }
1041         }
1042         
1043         if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1044                 
1045                 if (GTK_TOGGLE_BUTTON(both)->active) {
1046                         fp = fopen(g_dest, "a");
1047                         if (fp == NULL) {
1048                                 open_failure_alert_box(g_dest, errno, TRUE);
1049                                 return;
1050                         }
1051                         fprintf(fp, "\nReverse\n");
1052                         if (ferror(fp)) {
1053                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1054                                     file_write_error_message(errno), g_dest);
1055                                 fclose(fp);
1056                                 return;
1057                         }
1058                 } else {
1059                         fp = fopen(g_dest, "w");
1060                         if (fp == NULL) {
1061                                 open_failure_alert_box(g_dest, errno, TRUE);
1062                                 return;
1063                         }
1064                 }
1065                 for(j = 0; j < 8; j++) {
1066                         if (j == 0) {
1067                                 fprintf(fp,"%s",titles[j]);
1068                         } else {
1069                                 fprintf(fp,",%s",titles[j]);
1070                         }
1071                 }
1072                 fprintf(fp,"\n");
1073                 if (ferror(fp)) {
1074                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1075                             file_write_error_message(errno), g_dest);
1076                         fclose(fp);
1077                         return;
1078                 }
1079                 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1080                         for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1081                                 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1082                                 if (j == 0) {
1083                                         fprintf(fp,"%s",columnText);
1084                                 } else {
1085                                         fprintf(fp,",%s",columnText);
1086                                 }
1087                         }
1088                         fprintf(fp,"\n");
1089                         if (ferror(fp)) {
1090                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1091                                     file_write_error_message(errno), g_dest);
1092                                 fclose(fp);
1093                                 return;
1094                         }
1095                 }
1096                 if (fclose(fp) == EOF) {
1097                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1098                             file_write_error_message(errno), g_dest);
1099                         return;
1100                 }
1101         }
1102
1103         gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1104 }
1105
1106 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1107 {
1108         user_data->dlg.save_csv_as_w = NULL;
1109 }
1110
1111 /* when the user wants to save the csv information in a file */
1112 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
1113 {
1114         GtkWidget *vertb;
1115         GtkWidget *table1;
1116         GtkWidget *label_format;
1117         GtkWidget *channels_label;
1118         GSList *channels_group = NULL;
1119         GtkWidget *forward_rb;
1120         GtkWidget *reversed_rb;
1121         GtkWidget *both_rb;
1122         GtkWidget *ok_bt;
1123         
1124         if (user_data->dlg.save_csv_as_w != NULL) {
1125                 /* There's already a Save CSV info dialog box; reactivate it. */
1126                 reactivate_window(user_data->dlg.save_csv_as_w);
1127                 return;
1128         }
1129         
1130         user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
1131         SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
1132                        save_csv_as_destroy_cb, user_data);
1133         
1134         /* Container for each row of widgets */
1135         vertb = gtk_vbox_new(FALSE, 0);
1136         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1137         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
1138                 vertb, FALSE, FALSE, 0);
1139         gtk_widget_show (vertb);
1140         
1141         table1 = gtk_table_new (2, 4, FALSE);
1142         gtk_widget_show (table1);
1143         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1144         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1145         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1146         
1147         label_format = gtk_label_new ("Format: Comma Separated Values");
1148         gtk_widget_show (label_format);
1149         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1150                 (GtkAttachOptions) (GTK_FILL),
1151                 (GtkAttachOptions) (0), 0, 0);
1152         
1153         
1154         channels_label = gtk_label_new ("Channels:");
1155         gtk_widget_show (channels_label);
1156         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1157                 (GtkAttachOptions) (GTK_FILL),
1158                 (GtkAttachOptions) (0), 0, 0);
1159         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1160         
1161         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
1162         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1163         gtk_widget_show (forward_rb);
1164         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1165                 (GtkAttachOptions) (GTK_FILL),
1166                 (GtkAttachOptions) (0), 0, 0);
1167         
1168         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1169         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1170         gtk_widget_show (reversed_rb);
1171         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1172                 (GtkAttachOptions) (GTK_FILL),
1173                 (GtkAttachOptions) (0), 0, 0);
1174         
1175         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1176         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1177         gtk_widget_show (both_rb);
1178         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1179                 (GtkAttachOptions) (GTK_FILL),
1180                 (GtkAttachOptions) (0), 0, 0);
1181         
1182         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1183         
1184         ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
1185         OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1186         OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1187         OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1188         OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1189         
1190         /* Connect the cancel_button to destroy the widget */
1191         SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button,
1192                 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1193                 user_data->dlg.save_csv_as_w);
1194         
1195         /* Catch the "key_press_event" signal in the window, so that we can catch
1196         the ESC key being pressed and act as if the "Cancel" button had
1197         been selected. */
1198         dlg_set_cancel(user_data->dlg.save_csv_as_w, GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button);
1199         
1200         SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
1201                        user_data->dlg.save_csv_as_w);
1202         
1203         gtk_widget_show(user_data->dlg.save_csv_as_w);
1204 }
1205
1206
1207 /****************************************************************************/
1208 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1209 {
1210         /* Note that we no longer have a Save voice info dialog box. */
1211         user_data->dlg.save_voice_as_w = NULL;
1212 }
1213
1214 /****************************************************************************/
1215 /* here we save it into a file that user specified */
1216 /* XXX what about endians here? could go something wrong? */
1217 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
1218 {
1219         int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
1220         gint16 f_pd;
1221         gint16 r_pd;
1222         gchar pd[1];
1223         guint32 f_write_silence = 0;
1224         guint32 r_write_silence = 0;
1225         progdlg_t *progbar;
1226         guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
1227         gboolean stop_flag = FALSE;
1228
1229         forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
1230         if (forw_fd < 0) 
1231                 return FALSE;
1232         rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
1233         if (rev_fd < 0) {
1234                 close(forw_fd); 
1235                 return FALSE;
1236         }
1237
1238         /* open file for saving */
1239         to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1240         if (to_fd < 0) {
1241                 close(forw_fd);
1242                 close(rev_fd);
1243                 return FALSE;
1244         }
1245
1246         progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
1247
1248         /* First we write the .au header. XXX Hope this is endian independant */
1249         /* the magic word 0x2e736e64 == .snd */
1250         *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
1251         *pd = (unsigned char)0x73; write(to_fd, pd, 1);
1252         *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
1253         *pd = (unsigned char)0x64; write(to_fd, pd, 1);
1254         /* header offset == 24 bytes */
1255         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1256         write(to_fd, pd, 1);
1257         write(to_fd, pd, 1);
1258         *pd = (unsigned char)0x18; write(to_fd, pd, 1);
1259         /* total length, it is permited to set this to 0xffffffff */
1260         *pd = (unsigned char)0xff; write(to_fd, pd, 1); 
1261         write(to_fd, pd, 1); 
1262         write(to_fd, pd, 1); 
1263         write(to_fd, pd, 1);
1264         /* encoding format == 8 bit ulaw */
1265         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1266         write(to_fd, pd, 1);
1267         write(to_fd, pd, 1);
1268         *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1269         /* sample rate == 8000 Hz */
1270         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1271         write(to_fd, pd, 1);
1272         *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
1273         *pd = (unsigned char)0x40; write(to_fd, pd, 1);
1274         /* channels == 1 */
1275         *pd = (unsigned char)0x00; write(to_fd, pd, 1);
1276         write(to_fd, pd, 1);
1277         write(to_fd, pd, 1);
1278         *pd = (unsigned char)0x01; write(to_fd, pd, 1);
1279         
1280         switch (channels) {
1281                 /* only forward direction */
1282                 case 1: {
1283                         progbar_count = user_data->forward.saveinfo.count;
1284                         progbar_quantum = user_data->forward.saveinfo.count/100;
1285                         while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
1286                                 if(stop_flag) 
1287                                         break;
1288                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
1289                                         update_progress_dlg(progbar, 
1290                                                 (gfloat) count/progbar_count, "Saving");
1291                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
1292                                 }
1293                                 count++;
1294                                 *pd = (unsigned char)linear2ulaw(f_pd);
1295                                 fwritten = write(to_fd, pd, 1);
1296                                 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
1297                                         close(forw_fd);
1298                                         close(rev_fd);
1299                                         close(to_fd);
1300                                         destroy_progress_dlg(progbar);
1301                                         return FALSE;
1302                                 }
1303                         }
1304                         break;
1305                 }
1306                 /* only reversed direction */
1307                 case 2: {
1308                         progbar_count = user_data->reversed.saveinfo.count;
1309                         progbar_quantum = user_data->reversed.saveinfo.count/100;
1310                         while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
1311                                 if(stop_flag) 
1312                                         break;
1313                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
1314                                         update_progress_dlg(progbar, 
1315                                                 (gfloat) count/progbar_count, "Saving");
1316                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
1317                                 }
1318                                 count++;
1319                                 *pd = (unsigned char)linear2ulaw(r_pd);
1320                                 rwritten = write(to_fd, pd, 1);
1321                                 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
1322                                         close(forw_fd);
1323                                         close(rev_fd);
1324                                         close(to_fd);
1325                                         destroy_progress_dlg(progbar);
1326                                         return FALSE;
1327                                 }
1328                         }
1329                         break;
1330                 }
1331                 /* both directions */
1332                 default: {
1333                         (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ? 
1334                                         (progbar_count = user_data->forward.saveinfo.count) : 
1335                                                 (progbar_count = user_data->reversed.saveinfo.count);
1336                         progbar_quantum = progbar_count/100;
1337                         /* since conversation in one way can start later than in the other one, 
1338                          * we have to write some silence information for one channel */
1339                         if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
1340                                 f_write_silence = (guint32)
1341                                         ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
1342                         }
1343                         else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
1344                                 r_write_silence = (guint32)
1345                                         ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
1346                         }
1347                         for(;;) {
1348                                 if(stop_flag) 
1349                                         break;
1350                                 if((count > progbar_nextstep) && (count <= progbar_count)) {
1351                                         update_progress_dlg(progbar, 
1352                                                 (gfloat) count/progbar_count, "Saving");
1353                                         progbar_nextstep = progbar_nextstep + progbar_quantum;
1354                                 }
1355                                 count++;
1356                                 if(f_write_silence > 0) {
1357                                         rread = read(rev_fd, &r_pd, 2);
1358                                         f_pd = 0;
1359                                         fread = 1;
1360                                         f_write_silence--;
1361                                 }
1362                                 else if(r_write_silence > 0) {
1363                                         fread = read(forw_fd, &f_pd, 2);
1364                                         r_pd = 0;
1365                                         rread = 1;
1366                                         r_write_silence--;
1367                                 }
1368                                 else {
1369                                         fread = read(forw_fd, &f_pd, 2); 
1370                                         rread = read(rev_fd, &r_pd, 2);
1371                                 }
1372                                 if ((rread == 0) && (fread == 0)) 
1373                                         break;
1374                                 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
1375                                 rwritten = write(to_fd, pd, 1);
1376                                 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
1377                                         close(forw_fd);
1378                                         close(rev_fd);
1379                                         close(to_fd);
1380                                         destroy_progress_dlg(progbar);
1381                                         return FALSE;
1382                                 }
1383                         }
1384                 }
1385         }
1386         destroy_progress_dlg(progbar);
1387         close(forw_fd);
1388         close(rev_fd);
1389         close(to_fd);
1390         return TRUE;
1391 }
1392
1393
1394 /****************************************************************************/
1395 /* the user wants to save in a file */
1396 /* XXX support for different formats is currently commented out */
1397 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
1398 {
1399         gchar *g_dest;
1400         /*GtkWidget *wav, *au, *sw;*/
1401         GtkWidget *rev, *forw, *both;
1402         user_data_t *user_data;
1403         gint channels /*, format*/;
1404         
1405         g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1406         
1407         /* Perhaps the user specified a directory instead of a file.
1408         Check whether they did. */
1409         if (test_for_directory(g_dest) == EISDIR) {
1410                 /* It's a directory - set the file selection box to display it. */
1411                 set_last_open_dir(g_dest);
1412                 g_free(g_dest);
1413                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_open_dir);
1414                 return;
1415         }
1416         
1417         /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
1418         au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
1419         sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
1420         rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
1421         forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
1422         both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
1423         user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
1424         
1425         /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
1426         * we don't support that codec. So we pop up a warning. Maybe it would be better to
1427         * disable the ok button or disable the buttons for direction if only one is not ok. The
1428         * problem is if we open the save voice dialog and then click the refresh button and maybe 
1429         * the state changes, so we can't save anymore. In this case we should be able to update
1430         * the buttons. For now it is easier if we put the warning when the ok button is pressed.
1431         */
1432         
1433         /* we can not save in both dirctions */
1434         if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
1435                 /* there are many combinations here, we just exit when first matches */
1436                 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) || 
1437                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
1438                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1439                         "Can't save in a file: Unsupported codec!");
1440                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) || 
1441                         (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
1442                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1443                         "Can't save in a file: Wrong length of captured packets!");
1444                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) || 
1445                         (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
1446                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1447                         "Can't save in a file: RTP data with padding!");
1448                 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) || 
1449                         (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
1450                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1451                         "Can't save in a file: Not all data in all packets was captured!");
1452                 else  
1453                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1454                         "Can't save in a file: File I/O problem!");
1455                 return;
1456         }
1457         /* we can not save forward direction */
1458         else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
1459                 (GTK_TOGGLE_BUTTON (both)->active))) {  
1460                 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1461                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1462                         "Can't save forward direction in a file: Unsupported codec!");
1463                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1464                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1465                         "Can't save forward direction in a file: Wrong length of captured packets!");
1466                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1467                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1468                         "Can't save forward direction in a file: RTP data with padding!");
1469                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
1470                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1471                         "Can't save forward direction in a file: Not all data in all packets was captured!");
1472                 else
1473                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1474                         "Can't save forward direction in a file: File I/O problem!");
1475                 return;
1476         }
1477         /* we can not save reversed direction */
1478         else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
1479                 (GTK_TOGGLE_BUTTON (both)->active))) {  
1480                 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
1481                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1482                         "Can't save reversed direction in a file: Unsupported codec!");
1483                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
1484                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1485                         "Can't save reversed direction in a file: Wrong length of captured packets!");
1486                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
1487                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1488                         "Can't save reversed direction in a file: RTP data with padding!");
1489                 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
1490                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1491                         "Can't save reversed direction in a file: Not all data in all packets was captured!");
1492                 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
1493                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1494                         "Can't save reversed direction in a file: No RTP data!");
1495                 else
1496                         simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1497                         "Can't save reversed direction in a file: File I/O problem!");
1498                 return;
1499         }
1500         
1501         /*if (GTK_TOGGLE_BUTTON (wav)->active)
1502         format = 1;
1503         else if (GTK_TOGGLE_BUTTON (au)->active)
1504         format = 2;
1505         else if (GTK_TOGGLE_BUTTON (sw)->active)
1506         format = 3;*/
1507         
1508         if (GTK_TOGGLE_BUTTON (rev)->active)
1509                 channels = 2;
1510         else if (GTK_TOGGLE_BUTTON (both)->active)
1511                 channels = 3;
1512         else 
1513                 channels = 1;
1514         
1515         if(!copy_file(g_dest, channels/*, format*/, user_data)) {
1516                 /* XXX - report the error type! */
1517                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
1518                         "An error occured while saving voice in a file!");
1519                 return;
1520         }
1521         
1522         gtk_widget_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
1523 }
1524
1525 /****************************************************************************/
1526 /* when the user wants to save the voice information in a file */
1527 /* XXX support for different formats is currently commented out */
1528 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1529 {
1530         GtkWidget *vertb;
1531         GtkWidget *table1;
1532         GtkWidget *label_format;
1533         GtkWidget *channels_label;
1534         /*GSList *format_group = NULL;*/
1535         GSList *channels_group = NULL;
1536         GtkWidget *forward_rb;
1537         GtkWidget *reversed_rb;
1538         GtkWidget *both_rb;
1539         /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
1540         GtkWidget *ok_bt;
1541         
1542         /* if we can't save in a file: wrong codec, cut packets or other errors */
1543         /* shold the error arise here or later when you click ok button ? 
1544         * if we do it here, then we must disable the refresh button, so we don't do it here */
1545         
1546         if (user_data->dlg.save_voice_as_w != NULL) {
1547                 /* There's already a Save voice info dialog box; reactivate it. */
1548                 reactivate_window(user_data->dlg.save_voice_as_w);
1549                 return;
1550         }
1551         
1552         user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
1553         SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
1554                        save_voice_as_destroy_cb, user_data);
1555         
1556         /* Container for each row of widgets */
1557         vertb = gtk_vbox_new(FALSE, 0);
1558         gtk_container_border_width(GTK_CONTAINER(vertb), 5);
1559         gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
1560                 vertb, FALSE, FALSE, 0);
1561         gtk_widget_show (vertb);
1562         
1563         table1 = gtk_table_new (2, 4, FALSE);
1564         gtk_widget_show (table1);
1565         gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
1566         gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
1567         gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
1568         
1569         label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
1570         gtk_widget_show (label_format);
1571         gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
1572                 (GtkAttachOptions) (GTK_FILL),
1573                 (GtkAttachOptions) (0), 0, 0);
1574         
1575         /* we support .au - ulaw*/ 
1576         /*      wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
1577         format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
1578         gtk_widget_show (wav_rb);
1579         gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
1580         (GtkAttachOptions) (GTK_FILL),
1581         (GtkAttachOptions) (0), 0, 0);
1582         
1583           sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit  ");
1584           format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
1585           gtk_widget_show (sw_rb);
1586           gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
1587           (GtkAttachOptions) (GTK_FILL),
1588           (GtkAttachOptions) (0), 0, 0);
1589           au_rb = gtk_radio_button_new_with_label (format_group, ".au");
1590           format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
1591           gtk_widget_show (au_rb);
1592           gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
1593           (GtkAttachOptions) (GTK_FILL),
1594           (GtkAttachOptions) (0), 0, 0);
1595         */ 
1596         
1597         channels_label = gtk_label_new ("Channels:");
1598         gtk_widget_show (channels_label);
1599         gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
1600                 (GtkAttachOptions) (GTK_FILL),
1601                 (GtkAttachOptions) (0), 0, 0);
1602         gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
1603         
1604         forward_rb = gtk_radio_button_new_with_label (channels_group, "forward  ");
1605         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
1606         gtk_widget_show (forward_rb);
1607         gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
1608                 (GtkAttachOptions) (GTK_FILL),
1609                 (GtkAttachOptions) (0), 0, 0);
1610         
1611         reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
1612         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
1613         gtk_widget_show (reversed_rb);
1614         gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
1615                 (GtkAttachOptions) (GTK_FILL),
1616                 (GtkAttachOptions) (0), 0, 0);
1617         
1618         both_rb = gtk_radio_button_new_with_label (channels_group, "both");
1619         channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
1620         gtk_widget_show (both_rb);
1621         gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
1622                 (GtkAttachOptions) (GTK_FILL),
1623                 (GtkAttachOptions) (0), 0, 0);
1624         
1625         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
1626         
1627         /* if one direction is nok we don't allow saving 
1628         XXX this is not ok since the user can click the refresh button and cause changes
1629         but we can not update this window. So we move all the decision on the time the ok
1630         button is clicked
1631         if (user_data->forward.saved == FALSE) {
1632         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
1633         gtk_widget_set_sensitive(forward_rb, FALSE);
1634         gtk_widget_set_sensitive(both_rb, FALSE);
1635         }
1636         else if (user_data->reversed.saved == FALSE) {
1637         gtk_widget_set_sensitive(reversed_rb, FALSE);
1638         gtk_widget_set_sensitive(both_rb, FALSE);
1639         }
1640         */
1641         
1642         ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
1643         /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
1644         OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
1645         OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
1646         OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
1647         OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
1648         OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
1649         OBJECT_SET_DATA(ok_bt, "user_data", user_data);
1650         
1651         /* Connect the cancel_button to destroy the widget */
1652         SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button,
1653                 "clicked", (GtkSignalFunc)gtk_widget_destroy,
1654                 user_data->dlg.save_voice_as_w);
1655         
1656                 /* Catch the "key_press_event" signal in the window, so that we can catch
1657                 the ESC key being pressed and act as if the "Cancel" button had
1658         been selected. */
1659         dlg_set_cancel(user_data->dlg.save_voice_as_w, GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button);
1660         
1661         SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
1662                        user_data->dlg.save_voice_as_w);
1663         
1664         gtk_widget_show(user_data->dlg.save_voice_as_w);
1665 }
1666
1667
1668 /****************************************************************************/
1669 /* when we are finished with redisection, we add the label for the statistic */
1670 static void draw_stat(user_data_t *user_data)
1671 {
1672         gchar label_max[200];
1673         guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
1674                 - user_data->forward.statinfo.start_seq_nr + 1;
1675         guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
1676                 - user_data->reversed.statinfo.start_seq_nr + 1;
1677         gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
1678         gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
1679
1680         g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1681                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"
1682                 "   Sequence errors = %u",
1683                 user_data->forward.statinfo.max_delay, user_data->forward.statinfo.max_nr,
1684                 user_data->forward.statinfo.total_nr,
1685                 f_expected, f_lost, user_data->forward.statinfo.sequence);
1686
1687         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
1688
1689         g_snprintf(label_max, 199, "Max delay = %f sec at packet no. %u \n"
1690                 "Total RTP packets = %u   (expected %u)   Lost RTP packets = %d"
1691                 "   Sequence errors = %u",
1692                 user_data->reversed.statinfo.max_delay, user_data->reversed.statinfo.max_nr,
1693                 user_data->reversed.statinfo.total_nr,
1694                 r_expected, r_lost, user_data->reversed.statinfo.sequence);
1695
1696         gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
1697
1698         return ;
1699 }
1700
1701
1702 #define NUM_COLS 8
1703
1704 /****************************************************************************/
1705 /* append a line to clist */
1706 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
1707                          double delay, double jitter, gchar *status, gboolean marker,
1708                          gchar *timeStr, guint32 pkt_len, GdkColor *color)
1709 {
1710         guint added_row;
1711         gchar *data[8];
1712         gchar field[8][32];
1713
1714         data[0]=&field[0][0];
1715         data[1]=&field[1][0];
1716         data[2]=&field[2][0];
1717         data[3]=&field[3][0];
1718         data[4]=&field[4][0];
1719         data[5]=&field[5][0];
1720         data[6]=&field[6][0];
1721         data[7]=&field[7][0];
1722
1723         g_snprintf(field[0], 20, "%u", number);
1724         g_snprintf(field[1], 20, "%u", seq_num);
1725         g_snprintf(field[2], 20, "%f", delay);
1726         g_snprintf(field[3], 20, "%f", jitter);
1727         g_snprintf(field[4], 20, "%s", marker? "SET" : "");
1728         g_snprintf(field[5], 40, "%s", status);
1729         g_snprintf(field[6], 32, "%s", timeStr);
1730         g_snprintf(field[7], 20, "%u", pkt_len);
1731
1732         added_row = gtk_clist_append(GTK_CLIST(clist), data);
1733         gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
1734         gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
1735 }
1736
1737
1738 /****************************************************************************/
1739 /* callback for sorting columns of clist */
1740 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
1741 {
1742         char *text1 = NULL;
1743         char *text2 = NULL;
1744         int i1, i2;
1745         double f1, f2;
1746
1747         const GtkCListRow *row1 = (GtkCListRow *) ptr1;
1748         const GtkCListRow *row2 = (GtkCListRow *) ptr2;
1749
1750         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
1751         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
1752
1753         switch(clist->sort_column){
1754         /* columns representing strings */
1755         case 4:
1756         case 5:
1757         case 6:
1758                 return strcmp (text1, text2);
1759         /* columns representing ints */
1760         case 0:
1761         case 1:
1762         case 7:
1763                 i1=atoi(text1);
1764                 i2=atoi(text2);
1765                 return i1-i2;
1766         /* columns representing floats */
1767         case 2:
1768         case 3:
1769                 f1=atof(text1);
1770                 f2=atof(text2);
1771                 if (fabs(f1-f2)<0.0000005)
1772                         return 0;
1773                 if (f1<f2)
1774                         return -1;
1775                 return 1;
1776         }
1777         g_assert_not_reached();
1778         return 0;
1779 }
1780
1781
1782 /****************************************************************************/
1783 static void
1784 click_column_cb(GtkCList *clist, gint column, gpointer data)
1785 {
1786         column_arrows *col_arrows = (column_arrows *) data;
1787         int i;
1788
1789         gtk_clist_freeze(clist);
1790
1791         for (i = 0; i < NUM_COLS; i++) {
1792                 gtk_widget_hide(col_arrows[i].ascend_pm);
1793                 gtk_widget_hide(col_arrows[i].descend_pm);
1794         }
1795
1796         if (column == clist->sort_column) {
1797                 if (clist->sort_type == GTK_SORT_ASCENDING) {
1798                         clist->sort_type = GTK_SORT_DESCENDING;
1799                         gtk_widget_show(col_arrows[column].descend_pm);
1800                 } else {
1801                         clist->sort_type = GTK_SORT_ASCENDING;
1802                         gtk_widget_show(col_arrows[column].ascend_pm);
1803                 }
1804         } else {
1805                 clist->sort_type = GTK_SORT_ASCENDING;
1806                 gtk_widget_show(col_arrows[column].ascend_pm);
1807                 gtk_clist_set_sort_column(clist, column);
1808         }
1809         gtk_clist_thaw(clist);
1810
1811         gtk_clist_sort(clist);
1812 }
1813
1814
1815 /****************************************************************************/
1816 /* Add the packet list */
1817 static
1818 GtkWidget* create_clist(user_data_t* user_data)
1819 {
1820         GtkWidget* clist_fwd;
1821
1822         /* clist for the information */
1823         clist_fwd = gtk_clist_new(8);
1824         gtk_widget_show(clist_fwd);
1825         SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
1826
1827         gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
1828         gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
1829         gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
1830         gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
1831
1832         /* hide date and length column */
1833         gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 6, FALSE);
1834         gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
1835
1836         /* column widths and justification */
1837         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
1838         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
1839         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
1840         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
1841         gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
1842         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
1843         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
1844         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
1845         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
1846         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
1847         gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
1848
1849         return clist_fwd;
1850 }
1851
1852
1853 /****************************************************************************/
1854 /* Add the sort by column feature for a packet clist */
1855 static
1856 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
1857                                                                   user_data_t* user_data _U_)
1858 {
1859         column_arrows *col_arrows;
1860         GdkBitmap *ascend_bm, *descend_bm;
1861         GdkPixmap *ascend_pm, *descend_pm;
1862         GtkStyle *win_style;
1863         GtkWidget *column_lb;
1864         int i;
1865
1866         col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
1867         win_style = gtk_widget_get_style(window);
1868         ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1869                         &ascend_bm,
1870                         &win_style->bg[GTK_STATE_NORMAL],
1871                         (gchar **)clist_ascend_xpm);
1872         descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
1873                         &descend_bm,
1874                         &win_style->bg[GTK_STATE_NORMAL],
1875                         (gchar **)clist_descend_xpm);
1876
1877         for (i=0; i<NUM_COLS; i++) {
1878                 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1879                 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1880                 column_lb = gtk_label_new(titles[i]);
1881                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1882                 gtk_widget_show(column_lb);
1883
1884                 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
1885                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1886                 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
1887                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1888                 /* make packet-nr be the default sort order */
1889                 if (i == 0) {
1890                         gtk_widget_show(col_arrows[i].ascend_pm);
1891                 }
1892                 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
1893                 gtk_widget_show(col_arrows[i].table);
1894         }
1895
1896         SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
1897
1898         return col_arrows;
1899 }
1900
1901 /****************************************************************************/
1902 /* Create the dialog box with all widgets */
1903 void create_rtp_dialog(user_data_t* user_data)
1904 {
1905         GtkWidget *window = NULL;
1906         GtkWidget *clist_fwd;
1907         GtkWidget *clist_rev;
1908         GtkWidget *label_stats_fwd;
1909         GtkWidget *label_stats_rev;
1910         GtkWidget *notebook;
1911
1912         GtkWidget *main_vb, *page, *page_r;
1913         GtkWidget *label;
1914         GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
1915         GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
1916 #ifdef USE_CONVERSATION_GRAPH
1917         GtkWidget *graph_bt;
1918 #endif
1919
1920         gchar label_forward[150];
1921         gchar label_reverse[150];
1922
1923         gchar str_ip_src[16];
1924         gchar str_ip_dst[16];
1925         column_arrows *col_arrows_fwd;
1926         column_arrows *col_arrows_rev;
1927         
1928
1929         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1930         gtk_window_set_title (GTK_WINDOW (window), "Ethereal: RTP Stream Analysis");
1931         gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
1932         SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
1933
1934         /* Container for each row of widgets */
1935         main_vb = gtk_vbox_new(FALSE, 2);
1936         gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
1937         gtk_container_add(GTK_CONTAINER(window), main_vb);
1938         gtk_widget_show(main_vb);
1939
1940         /* Notebooks... */
1941         strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_fwd));
1942         strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_fwd));
1943
1944         g_snprintf(label_forward, 149, 
1945                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = %u", 
1946                 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
1947
1948         strcpy(str_ip_src, ip_to_str((ip_addr_p)&user_data->ip_src_rev));
1949         strcpy(str_ip_dst, ip_to_str((ip_addr_p)&user_data->ip_dst_rev));
1950
1951         g_snprintf(label_reverse, 149,
1952                 "Analysing stream from  %s port %u  to  %s port %u   SSRC = %u", 
1953                 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
1954
1955         /* Start a notebook for flipping between sets of changes */
1956         notebook = gtk_notebook_new();
1957         gtk_container_add(GTK_CONTAINER(main_vb), notebook);
1958         OBJECT_SET_DATA(window, "notebook", notebook);
1959         SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
1960                        user_data);
1961
1962         /* page for forward connection */
1963         page = gtk_vbox_new(FALSE, 8);
1964         gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1965
1966         /* direction label */
1967         label = gtk_label_new(label_forward);
1968         gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
1969
1970         /* place for some statistics */
1971         label_stats_fwd = gtk_label_new("\n");
1972         gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
1973
1974         /* scrolled window */
1975         scrolled_window = scrolled_window_new(NULL, NULL);
1976         WIDGET_SET_SIZE(scrolled_window, 560, 200);
1977
1978         /* packet clist */
1979         clist_fwd = create_clist(user_data);
1980         gtk_widget_show(clist_fwd);
1981         gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
1982         gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1983         gtk_widget_show(scrolled_window);
1984
1985         /* tab */
1986         label = gtk_label_new("  Forward Direction  ");
1987         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1988
1989         /* same page for reversed connection */
1990         page_r = gtk_vbox_new(FALSE, 8);
1991         gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
1992         label = gtk_label_new(label_reverse);
1993         gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
1994         label_stats_rev = gtk_label_new("\n");
1995         gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
1996
1997         scrolled_window_r = scrolled_window_new(NULL, NULL);
1998         WIDGET_SET_SIZE(scrolled_window_r, 560, 200);
1999
2000         clist_rev = create_clist(user_data);
2001         gtk_widget_show(clist_rev);
2002         gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
2003         gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
2004         gtk_widget_show(scrolled_window_r);
2005
2006         label = gtk_label_new("  Reversed Direction  ");
2007         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
2008
2009         /* page for help&about or future
2010         page_help = gtk_hbox_new(FALSE, 5);
2011         label = gtk_label_new("     Future    ");
2012         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
2013         frame = gtk_frame_new("");
2014         text = gtk_label_new("\n\nMaybe some more statistics: delay and jitter distribution,...");
2015         gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
2016         gtk_container_add(GTK_CONTAINER(frame), text);
2017         gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
2018         gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
2019         */
2020
2021         /* show all notebooks */
2022         gtk_widget_show_all(notebook);
2023
2024         /* buttons */
2025         box4 = gtk_hbutton_box_new();
2026         gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
2027         gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
2028         gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
2029         gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
2030         gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
2031         gtk_widget_show(box4);
2032
2033         voice_bt = gtk_button_new_with_label("Save payload...");
2034         gtk_container_add(GTK_CONTAINER(box4), voice_bt);
2035         gtk_widget_show(voice_bt);
2036         SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
2037
2038         csv_bt = gtk_button_new_with_label("Save as CSV...");
2039         gtk_container_add(GTK_CONTAINER(box4), csv_bt);
2040         gtk_widget_show(csv_bt);
2041         SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
2042
2043         refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
2044         gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
2045         gtk_widget_show(refresh_bt);
2046         SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
2047
2048         goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
2049         gtk_container_add(GTK_CONTAINER(box4), goto_bt);
2050         gtk_widget_show(goto_bt);
2051         SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
2052
2053 #ifdef USE_CONVERSATION_GRAPH
2054         graph_bt = gtk_button_new_with_label("Graph");
2055         gtk_container_add(GTK_CONTAINER(box4), graph_bt);
2056         gtk_widget_show(graph_bt);
2057         SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
2058 #endif
2059
2060         next_bt = gtk_button_new_with_label("Next non-Ok");
2061         gtk_container_add(GTK_CONTAINER(box4), next_bt);
2062         gtk_widget_show(next_bt);
2063         SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
2064
2065         close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
2066         gtk_container_add(GTK_CONTAINER(box4), close_bt);
2067         gtk_widget_show(close_bt);
2068         SIGNAL_CONNECT(close_bt, "clicked", on_close_bt_clicked, user_data);
2069
2070         gtk_widget_show(window);
2071
2072         /* sort by column feature */
2073         col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
2074         col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
2075
2076         /* some widget references need to be saved for outside use */
2077         user_data->dlg.window = window;
2078         user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
2079         user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
2080         user_data->dlg.label_stats_fwd = label_stats_fwd;
2081         user_data->dlg.label_stats_rev = label_stats_rev;
2082         user_data->dlg.notebook = notebook;
2083         user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
2084         user_data->dlg.selected_row = 0;
2085         user_data->dlg.col_arrows_fwd = col_arrows_fwd;
2086         user_data->dlg.col_arrows_rev = col_arrows_rev;
2087 }
2088
2089
2090 /****************************************************************************/
2091 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
2092                                                         gchar* proto_field, guint32* p_result)
2093 {
2094         field_info            *finfo;
2095         proto_node            *proto_sibling_node;
2096         header_field_info     *hfssrc;
2097         ipv4_addr             *ipv4;
2098
2099         finfo = PITEM_FINFO(ptree_node);
2100
2101         if (hfinformation==(finfo->hfinfo)) {
2102                 hfssrc = proto_registrar_get_byname(proto_field);
2103                 if (hfssrc == NULL)
2104                         return FALSE;
2105                 for(ptree_node=ptree_node->first_child; ptree_node!=NULL; 
2106                                         ptree_node=ptree_node->next) {
2107                         finfo=PITEM_FINFO(ptree_node);
2108                         if (hfssrc==finfo->hfinfo) {
2109                                 if (hfinformation->type==FT_IPv4) {
2110                                         ipv4 = fvalue_get(&finfo->value);
2111                                         *p_result = ipv4_get_net_order_addr(ipv4);
2112                                 }
2113                                 else {
2114                                         *p_result = fvalue_get_integer(&finfo->value);
2115                                 }
2116                                 return TRUE;
2117                         }
2118                 }
2119         }
2120
2121         proto_sibling_node = ptree_node->next;
2122
2123         if (proto_sibling_node) {
2124                 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
2125         }
2126         else
2127         return FALSE;
2128 }
2129
2130 /****************************************************************************/
2131 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
2132                                                                                          gchar* proto_name,
2133                                                                                          gchar* proto_field,
2134                                                                                          guint32* p_result)
2135 {
2136         proto_node      *ptree_node;
2137         header_field_info     *hfinformation;
2138
2139         hfinformation = proto_registrar_get_byname(proto_name);
2140         if (hfinformation == NULL)
2141                 return FALSE;
2142
2143         ptree_node = ((proto_node *)protocol_tree)->first_child;
2144         if (!ptree_node)
2145                 return FALSE;
2146
2147         return process_node(ptree_node, hfinformation, proto_field, p_result);
2148 }
2149
2150
2151 /* XXX just copied from gtk/rpc_stat.c */
2152 void protect_thread_critical_region(void);
2153 void unprotect_thread_critical_region(void);
2154
2155 /****************************************************************************/
2156 /* XXX only handles RTP over IPv4, should add IPv6 support */
2157 void rtp_analysis(
2158                 guint32 ip_src_fwd,
2159                 guint16 port_src_fwd,
2160                 guint32 ip_dst_fwd,
2161                 guint16 port_dst_fwd,
2162                 guint32 ssrc_fwd,
2163                 guint32 ip_src_rev,
2164                 guint16 port_src_rev,
2165                 guint32 ip_dst_rev,
2166                 guint16 port_dst_rev,
2167                 guint32 ssrc_rev
2168                 )
2169 {
2170         user_data_t *user_data;
2171         int fd;
2172
2173         /* init */
2174         user_data = g_malloc(sizeof(user_data_t));
2175
2176         user_data->ip_src_fwd = ip_src_fwd;
2177         user_data->port_src_fwd = port_src_fwd;
2178         user_data->ip_dst_fwd = ip_dst_fwd;
2179         user_data->port_dst_fwd = port_dst_fwd;
2180         user_data->ssrc_fwd = ssrc_fwd;
2181         user_data->ip_src_rev = ip_src_rev;
2182         user_data->port_src_rev = port_src_rev;
2183         user_data->ip_dst_rev = ip_dst_rev;
2184         user_data->port_dst_rev = port_dst_rev;
2185         user_data->ssrc_rev = ssrc_rev;
2186
2187         /* file names for storing sound data */
2188         /*XXX: check for errors*/
2189         fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
2190                 "ether_rtp_f");
2191         close(fd);
2192         fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
2193                 "ether_rtp_r");
2194         close(fd);
2195         user_data->forward.saveinfo.fp = NULL;
2196         user_data->reversed.saveinfo.fp = NULL;
2197         user_data->dlg.save_voice_as_w = NULL;
2198         user_data->dlg.save_csv_as_w = NULL;
2199 #ifdef USE_CONVERSATION_GRAPH
2200         user_data->dlg.graph_window = NULL;
2201         user_data->series_fwd.value_pairs = NULL;
2202         user_data->series_rev.value_pairs = NULL;
2203 #endif
2204
2205         /* create the dialog box */
2206         create_rtp_dialog(user_data);
2207
2208         /* proceed as if the Refresh button would have been pressed */
2209         on_refresh_bt_clicked(NULL, user_data);
2210 }
2211
2212 /****************************************************************************/
2213 /* entry point from main menu */
2214 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_) 
2215 {
2216         guint32 ip_src_fwd;
2217         guint16 port_src_fwd;
2218         guint32 ip_dst_fwd;
2219         guint16 port_dst_fwd;
2220         guint32 ssrc_fwd = 0;
2221         guint32 ip_src_rev;
2222         guint16 port_src_rev;
2223         guint32 ip_dst_rev;
2224         guint16 port_dst_rev;
2225         guint32 ssrc_rev = 0;
2226
2227         gchar filter_text[256];
2228         dfilter_t *sfcode;
2229         capture_file *cf;
2230         epan_dissect_t *edt;
2231         gint err;
2232         gchar *err_info;
2233         gboolean frame_matched;
2234         frame_data *fdata;
2235         GList *strinfo_list;
2236         GList *filtered_list = NULL;
2237         rtp_stream_info_t *strinfo;
2238         guint nfound;
2239
2240         /* Try to compile the filter. */
2241         strcpy(filter_text,"rtp && ip");
2242         if (!dfilter_compile(filter_text, &sfcode)) {
2243                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2244                     dfilter_error_msg);
2245                 return;
2246         }
2247         /* we load the current file into cf variable */
2248         cf = &cfile;
2249         fdata = cf->current_frame;
2250         
2251         /* we are on the selected frame now */
2252         if (fdata == NULL)
2253                 return; /* if we exit here it's an error */
2254
2255         /* dissect the current frame */
2256         if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
2257             cf->pd, fdata->cap_len, &err, &err_info)) {
2258                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2259                         cf_read_error_message(err, err_info), cf->filename);
2260                 return;
2261         }
2262         edt = epan_dissect_new(TRUE, FALSE);
2263         epan_dissect_prime_dfilter(edt, sfcode);
2264         epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
2265         frame_matched = dfilter_apply_edt(sfcode, edt);
2266         
2267         /* if it is not an rtp frame, show the rtpstream dialog */
2268         frame_matched = dfilter_apply_edt(sfcode, edt);
2269         if (frame_matched != 1) {
2270                 epan_dissect_free(edt);
2271                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2272                     "You didn't choose a RTP packet!");
2273                 return;
2274         }
2275
2276         /* ok, it is a RTP frame, so let's get the ip and port values */
2277         g_memmove(&ip_src_fwd, edt->pi.src.data, 4);
2278         g_memmove(&ip_dst_fwd, edt->pi.dst.data, 4);
2279         port_src_fwd = edt->pi.srcport;
2280         port_dst_fwd = edt->pi.destport;
2281
2282         /* assume the inverse ip/port combination for the reverse direction */
2283         g_memmove(&ip_src_rev, edt->pi.dst.data, 4);
2284         g_memmove(&ip_dst_rev, edt->pi.src.data, 4);
2285         port_src_rev = edt->pi.destport;
2286         port_dst_rev = edt->pi.srcport;
2287         
2288         /* now we need the SSRC value of the current frame */
2289         if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
2290                 simple_dialog(ESD_TYPE_ERROR | ESD_TYPE_MODAL, ESD_BTN_OK,
2291                     "SSRC value couldn't be found!");
2292                 return;
2293         }
2294
2295         /* search for reversed direction in the global rtp streams list */
2296         nfound = 0;
2297         strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
2298         while (strinfo_list)
2299         {
2300                 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
2301                 if (strinfo->src_addr==ip_src_fwd
2302                         && strinfo->src_port==port_src_fwd
2303                         && strinfo->dest_addr==ip_dst_fwd
2304                         && strinfo->dest_port==port_dst_fwd)
2305                 {
2306                         filtered_list = g_list_prepend(filtered_list, strinfo);
2307                 }
2308
2309                 if (strinfo->src_addr==ip_src_rev
2310                         && strinfo->src_port==port_src_rev
2311                         && strinfo->dest_addr==ip_dst_rev
2312                         && strinfo->dest_port==port_dst_rev)
2313                 {
2314                         ++nfound;
2315                         filtered_list = g_list_append(filtered_list, strinfo);
2316                         if (ssrc_rev==0)
2317                                 ssrc_rev = strinfo->ssrc;
2318                 }
2319
2320                 strinfo_list = g_list_next(strinfo_list);
2321         }
2322
2323         /* if more than one reverse streams found, we let the user choose the right one */
2324         if (nfound>1) {
2325                 rtpstream_dlg_show(filtered_list);
2326                 return;
2327         }
2328         else {
2329                 rtp_analysis(
2330                         ip_src_fwd,
2331                         port_src_fwd,
2332                         ip_dst_fwd,
2333                         port_dst_fwd,
2334                         ssrc_fwd,
2335                         ip_src_rev,
2336                         port_src_rev,
2337                         ip_dst_rev,
2338                         port_dst_rev,
2339                         ssrc_rev
2340                         );
2341         }
2342 }
2343
2344 /****************************************************************************/
2345 static void
2346 rtp_analysis_init(char *dummy _U_)
2347 {
2348         rtp_analysis_cb(NULL, NULL);
2349 }
2350
2351 /****************************************************************************/
2352 void
2353 register_tap_listener_rtp_analysis(void)
2354 {
2355         register_ethereal_tap("rtp", rtp_analysis_init);
2356 }
2357
2358 void
2359 register_tap_menu_rtp_analysis(void)
2360 {
2361         register_tap_menu_item("_Statistics/RTP Streams/Analyse...",
2362             rtp_analysis_cb, NULL, NULL, NULL);
2363 }