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