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