Ethereal-dev: Re: [Ethereal-dev] Plugin for making RTP analysis

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Miha Jemec <m.jemec@xxxxxxxxxxx>
Date: Wed, 15 Jan 2003 14:19:34 +0100
Hello again!

Guy Harris wrote:

On Wed, Jan 15, 2003 at 08:01:34AM +0100, Miha Jemec wrote:
I use Ethereal in my every day life a lot and you are doing a great job - thanks a lot. Since we use it for testing our VoIP equipment we added a "plugin" that makes RTP analysis and has the following features:

"Plugin" in the sense of a piece of code that gets loaded at run time? Or is this a compiled-in module?

Is this done with a tap?
For now it is not working as a plugin. It's just a piece of code you have to compile before starting the ethereal.

- it takes into account the silence surpression between frames
- it Replays the sound information:

"Replays" as in "plays through your sound card"?  If so, that could be
not only OS-dependent, but perhaps even, on UNIXes, dependent on your
desktop environment.
It goes through the sound card, so this part of code is for sure not yet portable to windows (I don't know about unix). But a college of mine will try do write this part for windows and should be ready in a few days.

Is there any interest from your side for this addition?

We'd be interested in seeing the code; either send it to ethereal-dev or
make it downloadable from an Internet site and send the URL to
ethereal-dev.  There is no "contact person" for Ethereal, there's just
ethereal-dev.
I attached the sources:

rtp.c - here is the code
rtp.h - declarations
menu.c - my example of changed toolbar
g711.c - routines that make the convertion between alaw (ulaw) and linear

I putted all the files in the gtk/ directory and changed the Makefiles (just added rtp.h and rtp.c)

So please tell me some comments about it and especially what can (should) be done. And of course what kind of interest could be from your side (as an addition, spin off project, nothing at all, expample how the code shouldn't be written,...)

Best regards,
Miha Jemec

p.s. I also attached the sources (sent_packet.h, sent_packet.c) of another addition, that makes possible to resend (send again on the wire with the same parameters) the captured frames. It is possible to resend only one frame (one that is selected) or to resend all the frames that are marked and choose to preserve the original gap between frames, or to resend the frames as fast as possible. Maybe someone could find this useful

/*
 * rtp.c
 *
 * RTP analysing addon tool for ethereal
 * Copyright 2002, Miha Jemec <m.jemec@xxxxxxxxxxx> Iskratel, Ltd, Kranj 
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


To do's & questions & problems:

- if the reversed connection doesn't use the same port and ip combination (same as forward direction but with inverted source and destination) it won't be displayed. Is it possible to use different ports for reversed direction? If yes there should also be this possibility.

- /dev/dsp doesn't return (immediately) BUSY state if it can't be opened, but it waits. Why?

- when playing forward or reversed sound it's possible that the progbar windows goes behind RTP_dialog_window if we click in this one. That shouldn't happen. 

- Also there is problem by replaying the voice with progbar_window because it starts already at the half way. Why? Buffer problem with audio device?

- for soundcard there is currently support only for AFMT_SE_16. What about AFMT_U8?

- in the case we act as a router (if we are doing the "Middle In the Man" attack) we catch an RTP packet and we forward it. That means we got two RTP packets with the same SSRC value. After doing RTP analysis on this packets, the results will be wrong! Solution 1: we can filter out only one conversation with the display filter like eth.dst == <MAC address follows>  or solution 2: instead of looking only for SSRC value and compare it, we could look also for source and destination MAC address

- i'm not sure how the silence for g.711 Alaw and ulaw should be written. Now there is value 0 inserted for silence. It works at least.

- problem with filter which let's some ICMP packet through RTP filter. Why?

- long, double, int... Should they go into guint32,...?

- more tests are required for: jitter, codecs, playing both directions, inserting silence and sinchronization, ...


*/



#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <gtk/gtk.h>
#include <g711.c>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#include "simple_dialog.h"
#include <epan/packet.h>
#include "ipproto.h"
#include "ui_util.h"
#include "progress_dlg.h"

#include <rtp.h>
#include "globals.h"
#include <epan/epan_dissect.h>

#include <sys/soundcard.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

extern GtkWidget *packet_list;
static GtkWidget *rtp_w;
static GtkWidget *main_vb;
static gchar source[30], destination[30];
static gchar ssrc_forward[30], ssrc_reversed[30];
static gint srcport, dstport;
char tempname_f[80], tempname_r[80];
FILE *fp_forward, *fp_reversed;
guint32 progbar_count;
double pkt_f_start, pkt_r_start, pkt_stop, pkt_start;


/* main fuction, when you press RTP in the menu bar */
void rtp_analyse_cb(GtkWidget *w _U_) {

  gchar filter_text[]="rtp";
  dfilter_t *sfcode;  
  gint i, err;
  gboolean display_info = FALSE;
  capture_file *cf;
  epan_dissect_t *edt;
  gboolean frame_matched;
  frame_data *fdata;
  gchar vrsta[150];
  gchar *p, *q;

  /* Try to compile the filter. */
  if (!dfilter_compile(filter_text, &sfcode)) {
	simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
	return;
  }

  /* There's already a "Display Options" dialog box; reactivate it. */
  if (rtp_w != NULL) {
    reactivate_window(rtp_w);
    return;
  }
 
  /* we load the current file into cf variable (could be done like in proto_hier_stat.c?) */
  cf = &cfile;
  fdata = cf->current_frame;

  /* we are on the selected frame now */
  if (fdata == NULL)
	return; /* if we exit here it's an error */ 

  /* dissect the current frame */
  wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, cf->pd, fdata->cap_len, &err);
  edt = epan_dissect_new(TRUE, FALSE);
  epan_dissect_prime_dfilter(edt, sfcode);
  epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
  frame_matched = dfilter_apply_edt(sfcode, edt);

  /* if it is not an rtp frame, exit */
  frame_matched = dfilter_apply_edt(sfcode, edt);
  if (frame_matched != 1) {
	epan_dissect_free(edt);
	simple_dialog(ESD_TYPE_CRIT, NULL, "You didn't choose a RTP packet!");
        return;
        }

  epan_dissect_fill_in_columns(edt);

  /* now we need the info column, because we get all the information from that line */
  for (i = 0; i < (cfile.cinfo.num_cols); i++){
	if(strcmp(cfile.cinfo.col_title[i], "Info")==0) {
		display_info = TRUE;
		break; /* let i on info-columnu */
	}
  }
  if (display_info == FALSE) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "You have to display Info column!");
	return;
  }

  /* get the ip and port values */
  srcport = edt->pi.srcport;
  dstport = edt->pi.destport;
  strncpy(source, ip_to_str(edt->pi.src.data), 19);
  strncpy(destination, ip_to_str(edt->pi.dst.data), 19);

  strncpy(vrsta, cfile.cinfo.col_data[i], 149);

  /* we need ssrc value */
  p = strstr(vrsta, "SSRC");
  if (p==NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Couldn't find SSRC value!");
        return;
  }
  p++; p++; p++; p++; p++;
  q = strstr(p, ",");
  *q = '\0'; 
  sprintf(ssrc_forward, "%s", p);

  /* clean up */
  epan_dissect_free(edt);

  /* let's draw the window */
  rtp_w = dlg_window_new("Ethereal: RTP Analyse");
  gtk_window_set_position (GTK_WINDOW (rtp_w), GTK_WIN_POS_CENTER);
  gtk_signal_connect(GTK_OBJECT(rtp_w), "destroy",
       GTK_SIGNAL_FUNC(rtp_destroy_cb), NULL);

  /* Container for each row of widgets */
  main_vb = gtk_vbox_new(FALSE, 3);
  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
  gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
  gtk_widget_show(main_vb);

  /* function for reversed ssrc */
  get_reversed_ssrc();

  /* and finally display this window */
  gtk_widget_show(rtp_w);
}



/* this function goes through all the packets that have the same ip and port combination (only reversed) as the forward direction (what if the reversed direction doesn't use the same ports???) and looks for different SSRC values. This can happen if you catch two RTP conversations one after another from the same pair of phones (PC's). Both have same IP's and can also have same port numbers, so they (should?) differ only in SSRC values. In such case we get a list of ssrc values and we have to choose the right one from the list. If there is only one or none, we skip it*/
static void get_reversed_ssrc(void) {

  GtkWidget *scroll_r, *clist_r, *ok_bt, *label, *label2, *label1, *main_hbnbox;
  capture_file *cf;
  frame_data *fdata;
  gint err, i, l = 0;
  gchar filter_text[150], temp[150], temp2[150];
  gchar *p, *q;
  dfilter_t *sfcode;
  epan_dissect_t *edt;
  gboolean frame_matched, found = FALSE;
  gchar **polje=NULL;
  progdlg_t *progbar;
  gboolean prog_flag;
  guint32 progbar_quantum, progbar_nextstep, count = 0;

  cf = &cfile;
  fdata = cf->plist;

  /* that is the filter we will look it for the packets. It has reversed IP and port combination*/
  sprintf(filter_text, "ip.src==%s and udp.srcport==%d and ip.dst==%s and udp.dstport==%d", destination, dstport, source, srcport);

  if (!dfilter_compile(filter_text, &sfcode)) {
        /* The attempt failed; report an error. */
        simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
        return;
        }

  progbar_nextstep = 0;
  progbar_quantum = cfile.count/100;
  prog_flag = FALSE;
  progbar = create_progress_dlg("Gathering information", "Stop", &prog_flag);

 /* scan all the packets and look for different ssrc values */
 for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {  

	if (count >= progbar_nextstep) {
                update_progress_dlg(progbar, (gfloat) count / cfile.count);
                progbar_nextstep += progbar_quantum;
        }
        count++;
        if (prog_flag==TRUE) {
		destroy_progress_dlg(progbar);
		gtk_widget_destroy(GTK_WIDGET(rtp_w));
                return;
	}

	/* we ignore packets that are not displayed */
        if(fdata->flags.passed_dfilter==0)
                continue;

        /* Load the frame from the capture file */
        wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
                        cf->pd, fdata->cap_len, &err);
        /* Dissect the frame */
        edt = epan_dissect_new(TRUE, FALSE);
        epan_dissect_prime_dfilter(edt, sfcode);
        epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);

        /* does the packet match our filter */
        frame_matched = dfilter_apply_edt(sfcode, edt);
        if (frame_matched != 1) {
                epan_dissect_free(edt);
                continue; 
	}
        epan_dissect_fill_in_columns(edt);
        strncpy(temp2, cf->cinfo.col_data[cf->cinfo.num_cols-1], 149);

        epan_dissect_free(edt);

 	p = strstr(temp2, "SSRC");
        /* well this can happen with rtp packets and unknown payload */ 
        if (p==NULL)
        	continue; 
        p++; p++; p++; p++; p++;
        q = strstr(p, ",");
        *q = '\0';

	/* do we already have this ssrc value in the list */
	for (i=0;  i<l; i++) {
		if (strcmp(*(polje+i), p) == 0) {
			found = TRUE;
			break;
		}
	}	
	/* if found == FALSE we have new value, let's store it */
	if (found == FALSE) {
		polje=(gchar**)realloc(polje, (i+1)*sizeof(gchar*));
                if (!polje) {
			simple_dialog(ESD_TYPE_CRIT, NULL, "Can't realloc");
                        return;
		}
		*(polje+i) = (gchar *)malloc(30*sizeof(gchar));
                if (!*(polje+i)) {
			simple_dialog(ESD_TYPE_CRIT, NULL, "Can't malloc");
                        return;
		}
		strcpy(*(polje+i), p);
		l++;
  		/* NULL at the end */
  		*(polje+l)=NULL;
	}
	else    /* if found == TRUE we already have this value */
		found = FALSE;
  }

  destroy_progress_dlg(progbar);

  /* now if l==0 - we don't have reversed connection, at least none is found
	 if l==1 - we have exactly one and that is ok
	 if l >0 - we have more than one, so we have to choose one from the list
  */
  if (l==0) {
	strcpy(ssrc_reversed, "No reversed connection!");
	add_rtp_notebook();
	return;
  }
  else if (l==1) {
	strcpy(ssrc_reversed, *polje);
	add_rtp_notebook();
	return;
  }
  else {
	/* make this dialog */
	label = gtk_label_new("Found more SSRC values for the reversed\n"
				"connection with following parameters:\n");
	sprintf(temp, "Source %s port %d Destination %s port %d", destination, dstport, source, srcport);
	label2 = gtk_label_new(temp);
	gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(main_vb), label2, FALSE, FALSE, 0);
	scroll_r = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_r), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	clist_r = gtk_clist_new(1);
	gtk_clist_set_column_width(GTK_CLIST(clist_r), 0, 80);
	gtk_container_add(GTK_CONTAINER(scroll_r), clist_r);
        gtk_box_pack_start(GTK_BOX(main_vb), scroll_r, TRUE, TRUE, 0);
	label1 = gtk_label_new("Select one value and click apply");
        gtk_box_pack_start(GTK_BOX(main_vb), label1, FALSE, FALSE, 0);
       
	main_hbnbox = gtk_hbutton_box_new();
	gtk_box_pack_start(GTK_BOX(main_vb), main_hbnbox, FALSE, TRUE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
	gtk_button_box_set_layout(GTK_BUTTON_BOX(main_hbnbox), GTK_BUTTONBOX_SPREAD);
	gtk_widget_show(main_hbnbox);

	ok_bt = gtk_button_new_with_label("Apply");
    	gtk_container_add(GTK_CONTAINER(main_hbnbox), ok_bt);
	gtk_signal_connect(GTK_OBJECT(clist_r), "select_row", GTK_SIGNAL_FUNC(get_selected_ssrc), NULL);
        gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked", GTK_SIGNAL_FUNC(apply_selected_ssrc), NULL);


	/* add all the ssrc values in the clist */
  	for (i=0; *(polje+i); i++) 
		gtk_clist_append(GTK_CLIST(clist_r), (polje+i));
	gtk_clist_select_row(GTK_CLIST(clist_r), 0, 0);

	gtk_widget_show(label);
	gtk_widget_show(label1);
	gtk_widget_show(label2);
        gtk_widget_show(ok_bt);
	gtk_widget_show(clist_r);
        gtk_widget_show(scroll_r);
  }
}


/* when we click on the selected row it copies that ssrc value into ssrc_reversed */
static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column, GdkEventButton *event, gpointer data)
{
	gchar *text;
	gtk_clist_get_text(GTK_CLIST(clist_r), row, column, &text);
	sprintf(ssrc_reversed, "%s", text);
	return;
} 

/* when we click apply button */
static void apply_selected_ssrc(void)
{
	gtk_widget_destroy(main_vb);
	main_vb = gtk_vbox_new(FALSE, 3);
	gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
	gtk_container_add(GTK_CONTAINER(rtp_w), main_vb);
 	gtk_widget_show(main_vb);
	add_rtp_notebook();
}


static void rtp_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
{
  fclose(fp_forward);
  fclose(fp_reversed);
  remove(tempname_f);
  remove(tempname_r);
  /* Note that we no longer have a "RTP Analyse" dialog box. */
  rtp_w = NULL;
}


/* function creates the notebook's pages */
static void add_rtp_notebook (void)
{
  GtkWidget *notebook, *page, *page1, *page2, *label, *label1, *label2, *label3, *label4, *scrolled_window, *scrolled_window1, *clist, *clist1, *play1_bt, *play2_bt, *play3_bt, *box4, *close_bn;
  gchar *titles[5] =  {"Packet nr.", "Sequence",  "Delay", "Jitter", "Status"};
  gchar label_forward[150];
  gchar label_reverse[150];
  gboolean return_f, return_r = FALSE;

  sprintf(label_forward, "Analysing connection from  %s port %d  to  %s port %d   SSRC = %s\n", source, srcport, destination, dstport, ssrc_forward);
  sprintf(label_reverse, "Analysing connection from  %s port %d  to  %s port %d   SSRC = %s\n", destination, dstport, source, srcport, ssrc_reversed);
 
  /* Start a nootbook for flipping between sets of changes */
  notebook = gtk_notebook_new();
  gtk_container_add(GTK_CONTAINER(main_vb), notebook);
  gtk_object_set_data(GTK_OBJECT(rtp_w), "notebook", notebook);

  /* page for forward connection */
  page = gtk_vbox_new(FALSE, 5);
  gtk_container_set_border_width(GTK_CONTAINER(page), 20);

  /* scrolled window */
  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
  gtk_widget_set_usize(scrolled_window, 550, 200);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

  /* direction label */
  label1 = gtk_label_new(label_forward);
  gtk_box_pack_start(GTK_BOX(page), label1, FALSE, FALSE, 0);

  /* clist for the information */ 
  clist = gtk_clist_new_with_titles(5, titles);
  gtk_widget_show(clist);
  gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
  gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);

  /* and the label */
  label = gtk_label_new("     Forward Direction     ");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);

  /* column width and justification */
  gtk_clist_set_column_width(GTK_CLIST(clist), 0, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist), 1, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist), 2, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist), 3, 80);
  gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER);
  
  /* page for reversed connection */
  page1 = gtk_vbox_new(FALSE, 5);
  gtk_container_set_border_width(GTK_CONTAINER(page1), 20);
  scrolled_window1 = gtk_scrolled_window_new(NULL, NULL);
  gtk_widget_set_usize(scrolled_window1, 550, 200);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window1), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  label3 = gtk_label_new(label_reverse);
  gtk_box_pack_start(GTK_BOX(page1), label3, FALSE, FALSE, 0);
  clist1 = gtk_clist_new_with_titles(5, titles);
  gtk_widget_show(clist1);
  gtk_container_add(GTK_CONTAINER(scrolled_window1), clist1);
  gtk_box_pack_start(GTK_BOX(page1), scrolled_window1, TRUE, TRUE, 0);
  label2 = gtk_label_new("     Reversed Direction     ");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page1, label2);

  gtk_clist_set_column_width(GTK_CLIST(clist1), 0, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist1), 1, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist1), 2, 80);
  gtk_clist_set_column_width(GTK_CLIST(clist1), 3, 80);
  gtk_clist_set_column_justification(GTK_CLIST(clist1), 0, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist1), 1, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist1), 2, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist1), 3, GTK_JUSTIFY_CENTER);
  gtk_clist_set_column_justification(GTK_CLIST(clist1), 4, GTK_JUSTIFY_CENTER);

  /* we open both files for sound */
  tmpnam(tempname_f);
  if((fp_forward = fopen(tempname_f, "w+")) == NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Can't open file for writing!");
	remove(tempname_f);
	return;
  }
  tmpnam(tempname_r);
  if((fp_reversed = fopen(tempname_r, "w+")) == NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Can't open file for writing!");
	fclose(fp_forward);
	remove(tempname_f);
	remove(tempname_r);
	return;
  }

  /* we call the function that makes the analysis */
  return_f = add_data(clist, page, ssrc_forward, "forward");
  /* if there is no reversed ssrc, skip it */
  if (strcmp(ssrc_reversed, "No reversed connection!")!=0)
	  return_r = add_data(clist1, page1, ssrc_reversed, "reversed");

  /* page for help&about */
  page2 = gtk_hbox_new(FALSE, 5);
  help_about_page(page2);
  label4 = gtk_label_new("     Help & About     ");
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page2, label4);

  /* show all notebooks */
  gtk_widget_show_all(notebook);

  /* and the close button */
  box4 = gtk_hbutton_box_new();
  gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
  gtk_button_box_set_layout(GTK_BUTTON_BOX(box4), GTK_BUTTONBOX_SPREAD);
  gtk_widget_show(box4);

  play1_bt = gtk_button_new_with_label("Play forward");
  gtk_container_add(GTK_CONTAINER(box4), play1_bt);
  gtk_widget_show(play1_bt);
  gtk_signal_connect(GTK_OBJECT(play1_bt), "clicked",
           GTK_SIGNAL_FUNC(play_bt_clicked), 1);
  if (return_f == FALSE)
	gtk_widget_set_sensitive(play1_bt, FALSE);

  play2_bt = gtk_button_new_with_label("Play reversed");
  gtk_container_add(GTK_CONTAINER(box4), play2_bt);
  gtk_widget_show(play2_bt);
  gtk_signal_connect(GTK_OBJECT(play2_bt), "clicked",
           GTK_SIGNAL_FUNC(play_bt_clicked), 2);
  if (return_r == FALSE)
	gtk_widget_set_sensitive(play2_bt, FALSE);

  play3_bt = gtk_button_new_with_label("Play both direction");
  gtk_container_add(GTK_CONTAINER(box4), play3_bt);
  gtk_widget_show(play3_bt);
  gtk_signal_connect(GTK_OBJECT(play3_bt), "clicked",
           GTK_SIGNAL_FUNC(play_bt_clicked), 3);
  if ((return_r == FALSE) || (return_f == FALSE))
	gtk_widget_set_sensitive(play3_bt, FALSE);

  close_bn = gtk_button_new_with_label("Close");
  gtk_container_add(GTK_CONTAINER(box4), close_bn);
  gtk_widget_show(close_bn);
  gtk_signal_connect(GTK_OBJECT(close_bn), "clicked",
           GTK_SIGNAL_FUNC(rtp_destroy), GTK_OBJECT(rtp_w));

}

static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w)
{
    gtk_grab_remove(GTK_WIDGET(parent_w));
    gtk_widget_destroy(GTK_WIDGET(parent_w));
}


/* get all the data for the notebook page and for the voice info */
gboolean add_data(gpointer clist, GtkWidget *page, gchar *ssrc_value, gchar *direction)
{
  guint l=0, s=0, timestamp_res=0;
  long stevec=0, stevec1=0, timestamp=0, timestamp1=0, timestamp_start=0, timestamp_stop=0;
  long fnumber=0, fnumber_all=0, fnumber_lost=0, fnumber_start=0, fnumber_stop=0, silence=0;
  double t1=0, t2=0, t3=0, j1=0, j2=0, max_delay = 0;
  gchar *p, *q;
  gchar temp[150], temp1[150], temp2[150], temp3[150];
  gchar *vrsta[5];
  gchar status_ok[]="OK";
  gchar status_ok_mark[]="OK, Marker bit set";
  gchar status_ok_start[]="Start";
  gchar status_nok[]="NOK - wrong sequence nr.";
  guint32 sec, usec;
  GtkWidget *max;
  frame_data *fdata;
  capture_file *cf;
  int err, codec_value, lost_rate, c, kl;
  gchar filter_text[40];
  dfilter_t *sfcode;
  epan_dissect_t *edt;
  gboolean frame_matched, forward_dir, play_sound = FALSE;
  progdlg_t *progbar;
  gboolean prog_flag;
  guint32 progbar_quantum, progbar_nextstep, count = 0;
  gint16 pd_silence=0, *pdsilence;
  char tempname[80];
  FILE *tmpfp;

  pdsilence = &pd_silence;  

  /* which direction */
  if (strcmp(direction, "forward")==0)
	forward_dir = TRUE;
  else
	forward_dir = FALSE;

  sprintf(filter_text, "rtp.ssrc==%s", ssrc_value);
  if (!dfilter_compile(filter_text, &sfcode)) {
        /* The attempt failed; report an error. */
        simple_dialog(ESD_TYPE_CRIT, NULL, dfilter_error_msg);
        return FALSE;
        }

  vrsta[0]= (gchar *)malloc(150*sizeof(gchar));
  vrsta[1]= (gchar *)malloc(150*sizeof(gchar));
  vrsta[2]= (gchar *)malloc(150*sizeof(gchar));
  vrsta[3]= (gchar *)malloc(150*sizeof(gchar));
  vrsta[4]= (gchar *)malloc(150*sizeof(gchar));

  cf = &cfile;
  fdata = cf->plist;

 /* Update the progress bar when it gets to this value. */
 progbar_nextstep = 0;
 progbar_quantum = cfile.count/100;
 prog_flag = FALSE;
 if(forward_dir==TRUE)
	progbar = create_progress_dlg("Computing forward RTP statistics", "Stop", &prog_flag);
 else
	progbar = create_progress_dlg("Computing reversed RTP statistics", "Stop", &prog_flag);

 /* scan all the packets */
 for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {

	if (count >= progbar_nextstep) {
        	update_progress_dlg(progbar, (gfloat) count / cfile.count);
                progbar_nextstep += progbar_quantum;
	}
	count++;
	if (prog_flag==TRUE)
		break;

	/* skip packets that are not displayed */
	if(fdata->flags.passed_dfilter==0)
		continue;

	/* Load the frame from the capture file */
        wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
                        cf->pd, fdata->cap_len, &err);

	/* Dissect the frame */
        edt = epan_dissect_new(TRUE, FALSE);
        epan_dissect_prime_dfilter(edt, sfcode);
        epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);

	/* does the packet match the filter */
        frame_matched = dfilter_apply_edt(sfcode, edt);
        if (frame_matched != 1) {
                epan_dissect_free(edt);
                continue;
                } 

        epan_dissect_fill_in_columns(edt);
	sprintf(vrsta[0], "%d", fdata->num);
        strncpy(temp, cf->cinfo.col_data[cf->cinfo.num_cols-1], 149);
        strncpy(temp1, cf->cinfo.col_data[cf->cinfo.num_cols-1], 149);
        strncpy(temp2, cf->cinfo.col_data[cf->cinfo.num_cols-1], 149);

	fnumber_all++; 

	/* which codec */
       	strncpy(temp3, cf->cinfo.col_data[cf->cinfo.num_cols-1], 149);
	p = strstr(temp3, "Payload type=");
	if (p==NULL) 
		continue;
	p = p + 19;
	q = strstr(p, ",");
	*q = '\0';

	/* we currently support only alaw, ulaw and g.723.1 */
	if(strcmp(p, "G.711 PCMA") == 0) {
		codec_value = 8;
		play_sound = TRUE;
	}
	else if (strcmp(p, "G.711 PCMU") == 0) {
		codec_value = 0;
		play_sound = TRUE;
	}	
	else if (strcmp(p, "G.723") == 0)
		codec_value = 4;
	else
		codec_value = -1; /* unknown value */

	/* MARK flag found, silence surpression... */
	p = strstr(temp, "Mark");
	if (p!=NULL)
		l = 0;

	/* first time and always when MARK flag is on */
	if (l==0) {  			
 	   /* remember the timestamp value */
	   p = strstr(temp1, "Time");
	   if (p==NULL) {
	   	simple_dialog(ESD_TYPE_CRIT, NULL, "No timestamp information in Info column!");
		free(vrsta[0]); free(vrsta[1]); free(vrsta[2]); free(vrsta[3]); free(vrsta[4]);
	   	return FALSE; 
	   }
	   p++; p++; p++; p++; p++;
	   q = strstr(p, ",");
	   if (q != NULL)
		*q = '\0';
	   timestamp1 =  atol(p);
	   timestamp_stop = timestamp1;

	   /* get the Seq number */
	   p = strstr(temp2, "Seq");
	   if (p==NULL)
	   	continue;
	   p++; p++; p++; p++;
	   q = strstr(p, ",");
	   *q = '\0';
	   stevec1 = atol(p);
	   fnumber_stop = stevec1; 
	   strcpy(vrsta[1], p);

	   /* time information */
	   sec = fdata->rel_secs;
	   usec = fdata->rel_usecs;
	   t2 = (double)sec + (double)usec/1000000;

	   /* if s==0 it means taht we have the first packet */
	   if (s==0) {
		if(forward_dir==TRUE)
			pkt_f_start = t2;	
		else
			pkt_r_start = t2;

		fnumber_start = stevec1;
		timestamp_start = timestamp1;
		timestamp_res = edt->pi.iplen - 40; /* timestamp resolution info */


		strcpy(vrsta[4],status_ok_start);
	   	sprintf(vrsta[2], "0"); /* first time is 0 */
	   	sprintf(vrsta[3], "0"); /* first time is 0 */
	   	j1 = 0;
	   	s=1;

/* what if I press the play both button, but there is (big) difference about the arrival of the first RTP packet for each direction. It means we have to check the relative time difference and insert enough silence, so that both direction will be sinchronozed (XXX after the first paket we forget about time arrivals of other packet; we asume all the next packets are equally delayed). We do this sinchronization when we analise the reversed connection, even if reversed connection starts earlier then forward (it means that we have to insert some silence at the beginning of the forward direction "file" (that is not very nice, but...) in case the forward direction starts earlier we just insert some silence at the beginning of the reversed voice "file" and then add the voice info (we could avoid this */

		if(forward_dir==FALSE) {
			/* if first RTP packet is from forward direction we just insert */
			/* silence at the beggining of the reversed file */
			if (pkt_r_start > pkt_f_start) { 
				silence = (long)((pkt_r_start - pkt_f_start)*8000);
				for(kl=0; kl < silence; kl++) 
			        	fwrite(pdsilence, 2, 2, fp_reversed);
			}
			/* if first RTP packet is from the reversed direction we have to add */
			/* silence at the beginning of the forward file. So first we copy */
			/* forward file in to one temp file, insert silence at the beginning */
			/* of the forward voice file and then copy it back. Not really nice */
			/* programing indeed ??? */
			else {
				pkt_start = pkt_r_start;
				rewind(fp_forward);
				tmpnam(tempname);
				tmpfp = fopen(tempname, "w+");
				/* copy everything in temp file */
				while(1) {
					c = fgetc(fp_forward);
					if (!feof(fp_forward))
						fputc(c, tmpfp);
					else
						break;
				}
				/* insert silence in forward file */
				silence = (long)((pkt_f_start - pkt_r_start)*8000);
				rewind(fp_forward);
				for(kl=0; kl < silence; kl++) 
			        	fwrite(pdsilence, 2, 2, fp_forward);
				rewind(tmpfp);
				/* copy from temp file back */
				while(1) {
					c = fgetc(tmpfp);
					if (!feof(tmpfp))
						fputc(c, fp_forward);
					else
						break;
				}
				fclose(tmpfp);
				remove(tempname);
			}
		}
		/* at forward just set the flag */
		else if (forward_dir == TRUE)
			pkt_start = pkt_f_start;

	   	convert_voice_data(cf->pd, (edt->pi.iplen - 40)/sizeof(guint8), 
				(fdata->cap_len - edt->pi.iplen + 40)/sizeof(guint8), 0, 
					codec_value, forward_dir);
	   }

 	   /* we are here every time the MARK bit is set */
	   else {
		strcpy(vrsta[4],status_ok_mark); /* XXX is seq ok? */
		t3 = t2 - t1;
		j2 = j1 + (fabs(t3-((double)timestamp1-(double)timestamp)/8000) - j1)/16;
		j1 = j2;
		sprintf(vrsta[2], "%f", t3); 
                sprintf(vrsta[3], "%f", j2); 

		/* is there any silence to insert */
		silence = ((timestamp1-timestamp)/(edt->pi.iplen - 40)) - 1;

		convert_voice_data(cf->pd, (edt->pi.iplen - 40)/sizeof(guint8), 
				(fdata->cap_len - edt->pi.iplen + 40)/sizeof(guint8), 
					silence, codec_value, forward_dir);
	   }

	   timestamp = timestamp1;
	   t1=t2; 
	   l=1;
	   stevec1++;

	}  /* end of first round */
	
	/* every other round */
	else {
	   /* we need timestamp info */
	   p = strstr(temp1, "Time");
	   if (p==NULL) 
	   	continue; /* XXX if we exit here is still eveything ok? */  
	   p++; p++; p++; p++; p++;
	   timestamp1 =  atol(p);
	   timestamp_stop = timestamp1;

	   /* delay and jitter calculation */
	   sec =  fdata->rel_secs;
	   usec =  fdata->rel_usecs;
	   t2 = (double)sec + (double)usec/1000000;
	   t3 = t2 -t1;
	   sprintf(vrsta[2], "%f", t3);
	   if (t3 > max_delay) {
		max_delay = t3;
		fnumber = (long)fdata->num;
	   }
	   t1 = t2;
	   j2 = j1 + (fabs(t3-((double)timestamp1-(double)timestamp)/8000) - j1)/16;
	   j1 = j2;
	   timestamp = timestamp1;
	   sprintf(vrsta[3], "%f", j2);

	   /* sequence number info */
	   p = strstr(temp2, "Seq");
	   if (p == NULL)
		continue;
	   p++; p++; p++; p++;
	   q = strstr(p, ",");
	   *q = '\0';
	   strcpy(vrsta[1], p);
	   stevec = atol(p);
	   fnumber_stop = stevec;  

	   convert_voice_data(cf->pd, (edt->pi.iplen - 40)/sizeof(guint8), 
			(fdata->cap_len - edt->pi.iplen + 40)/sizeof(guint8), 0, 
				codec_value, forward_dir);

	   /* based on the seq number we check if sequence is ok */
	   if (stevec == stevec1) 
	 	strcpy(vrsta[4],status_ok);
	   else {
		strcpy(vrsta[4], status_nok);
		stevec1 = stevec; /* if sequence is nok, we reset the seq number counter */
	   }
	   stevec1++;

	if(forward_dir==TRUE) 
		pkt_stop = t2;
	else {
		if (pkt_stop < t2)
			pkt_stop = t2;
	}
	
	}
	/* add the whole row */
	gtk_clist_append(GTK_CLIST(clist), vrsta);
	
	/* clean up */
	epan_dissect_free(edt);
  }

  destroy_progress_dlg(progbar);  

  /* information about all the packets, we need it for the voice playing progbar */
  progbar_count = (long)((pkt_stop - pkt_start)*8000);

  /* label for max delay */
  fnumber_lost = (fnumber_stop - fnumber_start + 1) - fnumber_all;

  /* here we get into troubles when we do it as a router and don't check the MAC values, 
  could go out if we enable MAC checking as well */
  if (fnumber_lost < 0)
	fnumber_lost = 0;
  lost_rate = fnumber_lost *100 / (fnumber_stop - fnumber_start + 1);

  sprintf(temp1, "Max delay = %f  at packet nr. %ld \n\n"
		"Total RTP packets = %ld   Lost RTP packets = %ld   Lost RTP rate = %d%c",
		 max_delay, fnumber, fnumber_all, fnumber_lost, lost_rate, '%');
  max = gtk_label_new(temp1);
  gtk_box_pack_end(GTK_BOX(page), max, FALSE, FALSE, 5);

  /* we return TRUE if at least one packet had g.711 alaw or ulaw codec */
  if (play_sound == TRUE) 
	return TRUE;
  else
	return FALSE;
}


/* well, here we convert g.711 alaw or ulaw to 16 bit integer and in case there was silence surpression enabled we also add silence bytes */
static void convert_voice_data(guint8 *pd, int length, int header_len, long silence, 
						int codec, gboolean direction) {

  int i, j;
  gint16 pd_silence=0;
  FILE *fp;
  gint16 *tmp, *pdsilence;

  if ((codec != 0) && (codec != 8))
	return; /* support only for alaw and ulaw */

  if ((tmp = malloc(4)) == NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Unable to allocate input/output buffer");
	return;
  }

  pdsilence = &pd_silence;

/* we move *pd for the length of all headers forward so it points at the beggining of RTP 
data.  XXX what if the header is different? Compressed RTP,...*/
  pd = pd + header_len; 

  if(direction == TRUE)
	fp = fp_forward;
  else
	fp = fp_reversed; 

  /* let's write some silence */
  for(j=0; j < silence; j++) {
		for(i=0; i<length; i++) 
			fwrite(pdsilence, 2, 2, fp);
  }

  for(i=0; i<length; i++, pd++) {
	if (codec == 8) { 
		*tmp =(gint16)alaw2linear((unsigned char)*pd);
		fwrite(tmp, 2, 2, fp);
	}
	else if (codec == 0) {
		*tmp =(gint16)ulaw2linear((unsigned char)*pd);
		fwrite(tmp, 2, 2, fp);
	}
  }  
  free(tmp);
}


/* we click the play button */
static void play_bt_clicked(GtkWidget *play_bt, gint direction) 
{
  FILE *fp, *fp1;
  gboolean stop_flag = FALSE, end_f = FALSE, end_r = FALSE;
  progdlg_t *progbar;
  guint32 progbar_quantum =0, progbar_nextstep = 0, count = 0;
  int handle = -1;
  gint16 *audiobuf, *audiobuf1;
  int rate = 8000;
  int channels = 0;         /* 0=mono 1=stereo */
  int format = AFMT_S16_LE;

  if (direction == 3)
	channels = 1;

  if ((audiobuf = malloc(4)) == NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Unable to allocate input/output buffer!");
	return;
  }

  if ((audiobuf1 = malloc(2)) == NULL) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Unable to allocate input/output buffer!");
  	free(audiobuf);
	return;
  }

  if ( (handle = open("/dev/dsp",O_WRONLY)) == -1 ) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Unable to open /dev/dsp!");
  	free(audiobuf);
  	free(audiobuf1);
	return;
  }

  if ( ioctl(handle, SNDCTL_DSP_STEREO, &channels) == -1 ) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Ioctl channels error!");
  	close(handle);
  	free(audiobuf);
  	free(audiobuf1);
	return;
  }

  if ( ioctl(handle, SNDCTL_DSP_SETFMT, &format) == -1 ) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Ioctl format error!");
  	close(handle);
  	free(audiobuf);
  	free(audiobuf1);
	return;
  }

  if ( ioctl(handle, SNDCTL_DSP_SPEED, &rate) == -1 ) {
	simple_dialog(ESD_TYPE_CRIT, NULL, "Ioctl speed rate error!");
  	close(handle);
  	free(audiobuf);
  	free(audiobuf1);
	return;
  }

  if (direction == 1) {
  	rewind(fp_forward);
	fp = fp_forward;
  	progbar = create_progress_dlg("Playing forward connection", "Stop", &stop_flag);
  }
  else if (direction == 2) {
  	rewind(fp_reversed);
	fp = fp_reversed;
  	progbar = create_progress_dlg("Playing reversed connection", "Stop", &stop_flag);
  }

  else {
  	rewind(fp_forward);
  	rewind(fp_reversed);
	fp = fp_forward;
	fp1 = fp_reversed;
  	progbar = create_progress_dlg("Playing both directions", "Stop", &stop_flag);
  }
  progbar_quantum = progbar_count/100;

  if (direction != 3) {
	while(fread(audiobuf, 2, 2, fp) == 2) {
		if(stop_flag) {
  			ioctl(handle, SNDCTL_DSP_RESET, 0); 
			break; 
		}
		if((count > progbar_nextstep) && (count <= progbar_count)) {
			update_progress_dlg(progbar, (gfloat) count/progbar_count);
			progbar_nextstep = progbar_nextstep + progbar_quantum;
		}
		write(handle, audiobuf, 2);
		count++;
	}
  }
  else {
	while(1) {
		if (fread(audiobuf, 2, 2, fp) != 2) {
			end_f = TRUE; 
			*audiobuf = 0;
		}
		if (fread(audiobuf1, 2, 2, fp1) != 2) {
			end_r = TRUE;
			*audiobuf1 = 0;
		}
		if ((end_f == TRUE) && (end_r == TRUE))
			break;
		if(stop_flag) {
  			ioctl(handle, SNDCTL_DSP_RESET, 0); 
			break; 
		}
		if((count > progbar_nextstep) && (count <= progbar_count)) {
			update_progress_dlg(progbar, (gfloat) count/progbar_count);
			progbar_nextstep = progbar_nextstep + progbar_quantum;
		}
		audiobuf++;
		*audiobuf = *audiobuf1;
		audiobuf--;
		write(handle, audiobuf, 4);
		count++;
	}
  }

  close(handle);
  destroy_progress_dlg(progbar);
  free(audiobuf);
  free(audiobuf1);

}

static void help_about_page(GtkWidget *page2)
{
  GtkWidget *text, *frame;

  frame = gtk_frame_new("");
  text = gtk_label_new(	
"Some Help notes:\n" 
"- Playing g.723.1 doesn't work. Why? It's about 20.000$ worth.\n"
"- Max delay means max delay between consecutive frames, so if the next frame is one with the\n"
"  Mark bit set (that means that silence detection was enabled) it is not calculated in the delay\n"
"- Jitter is calculated the same as in the RTCP calculation\n"
"- After the wrong sequence number, the counter is reseted, that means that counter is set to\n"
"  the current seq. number, so next seq. number should be curent++\n"
"ToDo's & Problems:\n"
"- Problems arise if you catch more RTP packets with same SSRC and same seq. number (it's not \n"
"  necessary that this are duplicated packet's because they can have different src and dst MAC\n"
"   or IP - this can happen if you act as a router)\n"
"\nAll the questions please send to:\n"
"m.jemec@xxxxxxxxxxx!\n");
  gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
  gtk_container_add(GTK_CONTAINER(frame), text);
  gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
  gtk_box_pack_start(GTK_BOX(page2), frame, TRUE, TRUE, 0);  
}
/*
 * rtp.h
 *
 * Declarations for RTP analysing addon tool for ethereal
 * Copyright 2002, Miha Jemec <m.jemec@xxxxxxxxxxx> Iskratel, Ltd, Kranj
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

void rtp_analyse_cb(GtkWidget *w _U_);
static void add_rtp_notebook (void);
static void rtp_destroy_cb(GtkWidget *, gpointer);
gboolean add_data(gpointer clist, GtkWidget *page, gchar *ssrc_value, gchar *direction);
static void get_reversed_ssrc();
static void apply_selected_ssrc(void);
static void get_selected_ssrc(GtkWidget *clist_r, gint row, gint column, GdkEventButton *event, gpointer data);
static void rtp_destroy (GtkWidget *close_bt _U_, gpointer parent_w);
static void convert_voice_data(guint8 *pd, int length, int header_len, long silence, int codec, gboolean);
static void help_about_page(GtkWidget *page2);
static void play_bt_clicked(GtkWidget *, gint direction);
/* menu.c
 * Menu routines
 *
 * $Id: menu.c,v 1.63 2002/05/03 21:55:15 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <gtk/gtk.h>
#include <glib.h>

#include <string.h>
#include <stdio.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include "../menu.h"

#include "main.h"
#include "menu.h"
#include <epan/packet.h>
#include <epan/resolv.h>
#include "prefs.h"
#include "capture_dlg.h"
#include "color_dlg.h"
#include "file_dlg.h"
#include "filter_prefs.h"
#include "find_dlg.h"
#include "goto_dlg.h"
#include "summary_dlg.h"
#include "display_opts.h"
#include "prefs_dlg.h"
#include "packet_win.h"
#include "print.h"
#include "follow_dlg.h"
#include "decode_as_dlg.h"
#include "help_dlg.h"
#include "proto_dlg.h"
#include "proto_hier_stats_dlg.h"
#include "keys.h"
#include <epan/plugins.h>
#include "tcp_graph.h"
#include <epan/epan_dissect.h>
#include <rtp.h>
#include <sent_packet.h>

GtkWidget *popup_menu_object;

#define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))

static void menus_init(void);
static void set_menu_sensitivity (gchar *, gint);

/* This is the GtkItemFactoryEntry structure used to generate new menus.
       Item 1: The menu path. The letter after the underscore indicates an
               accelerator key once the menu is open.
       Item 2: The accelerator key for the entry
       Item 3: The callback function.
       Item 4: The callback action.  This changes the parameters with
               which the function is called.  The default is 0.
       Item 5: The item type, used to define what kind of an item it is.
               Here are the possible values:

               NULL               -> "<Item>"
               ""                 -> "<Item>"
               "<Title>"          -> create a title item
               "<Item>"           -> create a simple item
               "<CheckItem>"      -> create a check item
               "<ToggleItem>"     -> create a toggle item
               "<RadioItem>"      -> create a radio item
               <path>             -> path of a radio item to link against
               "<Separator>"      -> create a separator
               "<Branch>"         -> create an item to hold sub items (optional)
               "<LastBranch>"     -> create a right justified branch 
    */

/* main menu */
static GtkItemFactoryEntry menu_items[] =
{
  {"/_File", NULL, NULL, 0, "<Branch>" },
  {"/File/_Open...", "<control>O", GTK_MENU_FUNC(file_open_cmd_cb), 0, NULL},
  {"/File/_Close", "<control>W", GTK_MENU_FUNC(file_close_cmd_cb), 0, NULL},
  {"/File/_Save", "<control>S", GTK_MENU_FUNC(file_save_cmd_cb), 0, NULL},
  {"/File/Save _As...", NULL, GTK_MENU_FUNC(file_save_as_cmd_cb), 0, NULL},
  {"/File/_Reload", "<control>R", GTK_MENU_FUNC(file_reload_cmd_cb), 0, NULL},
  {"/File/<separator>", NULL, NULL, 0, "<Separator>"},
  {"/File/_Print...", NULL, GTK_MENU_FUNC(file_print_cmd_cb), 0, NULL},
  {"/File/Print Pac_ket", "<control>P", GTK_MENU_FUNC(file_print_packet_cmd_cb), 0, NULL},
  {"/File/<separator>", NULL, NULL, 0, "<Separator>"},
  {"/File/_Quit", "<control>Q", GTK_MENU_FUNC(file_quit_cmd_cb), 0, NULL},
  {"/_Edit", NULL, NULL, 0, "<Branch>" },
#if 0
  /* Un-#if this when we actually implement Cut/Copy/Paste. */
  {"/Edit/Cut", "<control>X", NULL, 0, NULL},
  {"/Edit/Copy", "<control>C", NULL, 0, NULL},
  {"/Edit/Paste", "<control>V", NULL, 0, NULL},
  {"/Edit/<separator>", NULL, NULL, 0, "<Separator>"},
#endif
  {"/Edit/_Find Frame...", "<control>F", GTK_MENU_FUNC(find_frame_cb), 0, NULL},
  {"/Edit/Find _Next", "<control>N", GTK_MENU_FUNC(find_next_cb), 0, NULL},
  {"/Edit/Find _Previous", "<control>B", GTK_MENU_FUNC(find_previous_cb), 0, NULL},
  {"/Edit/_Go To Frame...", "<control>G", GTK_MENU_FUNC(goto_frame_cb), 0, NULL},
  {"/Edit/<separator>", NULL, NULL, 0, "<Separator>"},
  {"/Edit/_Mark Frame", "<control>M", GTK_MENU_FUNC(mark_frame_cb), 0, NULL},
  {"/Edit/Mark _All Frames", NULL, GTK_MENU_FUNC(mark_all_frames_cb), 0, NULL},
  {"/Edit/_Unmark All Frames", NULL, GTK_MENU_FUNC(unmark_all_frames_cb), 0, NULL},
  {"/Edit/<separator>", NULL, NULL, 0, "<Separator>"},
  {"/Edit/_Preferences...", NULL, GTK_MENU_FUNC(prefs_cb), 0, NULL},
#ifdef HAVE_LIBPCAP
  {"/Edit/_Capture Filters...", NULL, GTK_MENU_FUNC(cfilter_dialog_cb), 0, NULL},
#endif
  {"/Edit/_Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL},
  {"/Edit/P_rotocols...", NULL, GTK_MENU_FUNC(proto_cb), 0, NULL},
#ifdef HAVE_LIBPCAP
  {"/_Capture", NULL, NULL, 0, "<Branch>" },
  {"/Capture/_Start...", "<control>K", GTK_MENU_FUNC(capture_prep_cb), 0, NULL},
  /*
   * XXX - this doesn't yet work in Win32.
   */
#ifndef _WIN32
  {"/Capture/S_top", "<control>E", GTK_MENU_FUNC(capture_stop_cb), 0, NULL},
#endif /* _WIN32 */
#endif /* HAVE_LIBPCAP */
  {"/_Display", NULL, NULL, 0, "<Branch>" },
  {"/Display/_Options...", NULL, GTK_MENU_FUNC(display_opt_cb), 0, NULL},
  {"/Display/_Match", NULL, NULL, 0, "<Branch>" },
  {"/Display/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace), 0, NULL},
  {"/Display/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not), 0, NULL},
  {"/Display/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and), 0, NULL},
  {"/Display/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or), 0, NULL},
  {"/Display/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_not), 0, NULL},
  {"/Display/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_not), 0, NULL},
  {"/Display/_Prepare", NULL, NULL, 0, "<Branch>" },
  {"/Display/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace), 0, NULL},
  {"/Display/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not), 0, NULL},
  {"/Display/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and), 0, NULL},
  {"/Display/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or), 0, NULL},
  {"/Display/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_not), 0, NULL},
  {"/Display/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_not), 0, NULL},
  {"/Display/_Colorize Display...", NULL, GTK_MENU_FUNC(color_display_cb), 0, NULL},
  {"/Display/Collapse _All", NULL, GTK_MENU_FUNC(collapse_all_cb), 0, NULL},
  {"/Display/_Expand All", NULL, GTK_MENU_FUNC(expand_all_cb), 0, NULL},
  {"/Display/_Show Packet In New Window", NULL, GTK_MENU_FUNC(new_window_cb), 0, NULL},
  {"/Display/User Specified Decodes...", NULL, GTK_MENU_FUNC(decode_show_cb), 0, NULL},
  {"/_Tools", NULL, NULL, 0, "<Branch>" },
#ifdef HAVE_PLUGINS
  {"/Tools/_Plugins...", NULL, GTK_MENU_FUNC(tools_plugins_cmd_cb), 0, NULL},
#endif
  {"/Tools/_Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
  {"/Tools/_Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
/*  {"/Tools/Graph", NULL, NULL, 0, NULL}, future use */
  {"/_Tools/TCP Stream Analysis", NULL, NULL, 0, "<Branch>" },
  {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (Stevens)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 0, NULL},
  {"/_Tools/TCP Stream Analysis/Time-Sequence Graph (tcptrace)", NULL, GTK_MENU_FUNC (tcp_graph_cb), 1, NULL},
  {"/_Tools/TCP Stream Analysis/Throughput Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 2, NULL},
  {"/_Tools/TCP Stream Analysis/RTT Graph", NULL, GTK_MENU_FUNC (tcp_graph_cb), 3, NULL},
  {"/Tools/_Summary", NULL, GTK_MENU_FUNC(summary_open_cb), 0, NULL},
  {"/Tools/Protocol Hierarchy Statistics", NULL, GTK_MENU_FUNC(proto_hier_stats_cb), 0, NULL},

  {"/_Advanced", NULL, NULL, 0, "<Branch>" },
  {"/Advanced/_RTP Analysis", NULL, GTK_MENU_FUNC(rtp_analyse_cb), 0, NULL},
  {"/Advanced/_Sent selected packet", NULL, GTK_MENU_FUNC(sent_selected_cb), 0, NULL},
  {"/Advanced/_Sent marked packets (no timing)", NULL, GTK_MENU_FUNC(sent_marked_cb), 0, NULL},
  {"/Advanced/_Sent marked packets (preserve timing)", NULL, GTK_MENU_FUNC(sent_marked_with_cb), 0, NULL},
  {"/_Help", NULL, NULL, 0, "<LastBranch>" },
  {"/Help/_Help", NULL, GTK_MENU_FUNC(help_cb), 0, NULL},
  {"/Help/<separator>", NULL, NULL, 0, "<Separator>"},
  {"/Help/_About Ethereal...", NULL, GTK_MENU_FUNC(about_ethereal), 0, NULL}
};

/* calculate the number of menu_items */
static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);

/* packet list popup */
static GtkItemFactoryEntry packet_list_menu_items[] =
{
	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
	{"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL},
	{"/<separator>", NULL, NULL, 0, "<Separator>"},
	{"/Mark Frame", NULL, GTK_MENU_FUNC(mark_frame_cb), 0, NULL},
        {"/Match", NULL, NULL, 0, "<Branch>" },
        {"/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace2), 0, NULL},
        {"/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not2), 0, NULL},
        {"/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and2), 0, NULL},
        {"/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or2), 0, NULL},
        {"/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_not2), 0, NULL},
        {"/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_not2), 0, NULL},
        {"/Prepare", NULL, NULL, 0, "<Branch>" },
        {"/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace2), 0, NULL},
        {"/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not2), 0, NULL},
        {"/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and2), 0, NULL},
        {"/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or2), 0, NULL},
        {"/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_not2), 0, NULL},
        {"/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_not2), 0, NULL},
	{"/<separator>", NULL, NULL, 0, "<Separator>"},
	{"/Colorize Display...", NULL, GTK_MENU_FUNC(color_display_cb), 0, NULL},
	{"/Print...", NULL, GTK_MENU_FUNC(file_print_cmd_cb), 0, NULL},
  	{"/Print Packet", NULL, GTK_MENU_FUNC(file_print_packet_cmd_cb), 0, NULL},
  	{"/Show Packet In New Window", NULL, GTK_MENU_FUNC(new_window_cb), 0, NULL}, 
};

static GtkItemFactoryEntry tree_view_menu_items[] =
{
	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
	{"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL},
	{"/<separator>", NULL, NULL, 0, "<Separator>"},
	{"/Resolve Name", NULL, GTK_MENU_FUNC(resolve_name_cb), 0, NULL},
	{"/Protocol Properties...", NULL, GTK_MENU_FUNC(properties_cb), 0, NULL},
        {"/Match", NULL, NULL, 0, "<Branch>" },
        {"/Match/_Selected", NULL, GTK_MENU_FUNC(match_selected_cb_replace), 0, NULL},
        {"/Match/_Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_not), 0, NULL},
        {"/Match/_And Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and), 0, NULL},
        {"/Match/_Or Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or), 0, NULL},
        {"/Match/A_nd Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_and_not), 0, NULL},
        {"/Match/O_r Not Selected", NULL, GTK_MENU_FUNC(match_selected_cb_or_not), 0, NULL},
        {"/Prepare", NULL, NULL, 0, "<Branch>" },
        {"/Prepare/_Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_replace), 0, NULL},
        {"/Prepare/_Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_not), 0, NULL},
        {"/Prepare/_And Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and), 0, NULL},
        {"/Prepare/_Or Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or), 0, NULL},
        {"/Prepare/A_nd Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_and_not), 0, NULL},
        {"/Prepare/O_r Not Selected", NULL, GTK_MENU_FUNC(prepare_selected_cb_or_not), 0, NULL},
	{"/<separator>", NULL, NULL, 0, "<Separator>"},
	{"/Collapse All", NULL, GTK_MENU_FUNC(collapse_all_cb), 0, NULL},
	{"/Expand All", NULL, GTK_MENU_FUNC(expand_all_cb), 0, NULL}
};

static GtkItemFactoryEntry hexdump_menu_items[] =
{
	{"/Follow TCP Stream", NULL, GTK_MENU_FUNC(follow_stream_cb), 0, NULL},
	{"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, NULL},
	{"/Display Filters...", NULL, GTK_MENU_FUNC(dfilter_dialog_cb), 0, NULL}
};

static int initialize = TRUE;
static GtkItemFactory *factory = NULL;
static GtkItemFactory *packet_list_menu_factory = NULL;
static GtkItemFactory *tree_view_menu_factory = NULL;
static GtkItemFactory *hexdump_menu_factory = NULL;

static GSList *popup_menu_list = NULL;

static GtkAccelGroup *grp;

void
get_main_menu(GtkWidget ** menubar, GtkAccelGroup ** table) {

  grp = gtk_accel_group_new();

  if (initialize) {
    popup_menu_object = gtk_widget_new(GTK_TYPE_WIDGET, NULL);
    menus_init();
  }

  if (menubar)
    *menubar = factory->widget;

  if (table)
    *table = grp;
}

static void
menus_init(void) {

  if (initialize) {
    initialize = FALSE;

    /* popup */

    packet_list_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
    gtk_item_factory_create_items_ac(packet_list_menu_factory, sizeof(packet_list_menu_items)/sizeof(packet_list_menu_items[0]), packet_list_menu_items, popup_menu_object, 2);
    gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY, packet_list_menu_factory->widget);
    popup_menu_list = g_slist_append((GSList *)popup_menu_list, packet_list_menu_factory);

    tree_view_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
    gtk_item_factory_create_items_ac(tree_view_menu_factory, sizeof(tree_view_menu_items)/sizeof(tree_view_menu_items[0]), tree_view_menu_items, popup_menu_object, 2);
    gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY, tree_view_menu_factory->widget);
    popup_menu_list = g_slist_append((GSList *)popup_menu_list, tree_view_menu_factory);

    hexdump_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
    gtk_item_factory_create_items_ac(hexdump_menu_factory, sizeof(hexdump_menu_items)/sizeof(hexdump_menu_items[0]), hexdump_menu_items, popup_menu_object, 2);
    gtk_object_set_data(GTK_OBJECT(popup_menu_object), PM_HEXDUMP_KEY, hexdump_menu_factory->widget);
    popup_menu_list = g_slist_append((GSList *)popup_menu_list, hexdump_menu_factory);
    
    factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", grp);
    gtk_item_factory_create_items_ac(factory, nmenu_items, menu_items, NULL,2);
    set_menus_for_unsaved_capture_file(FALSE);
    set_menus_for_capture_file(FALSE);
#if 0
    /* Un-#if this when we actually implement Cut/Copy/Paste.
       Then make sure you enable them when they can be done. */
    set_menu_sensitivity("/Edit/Cut", FALSE);
    set_menu_sensitivity("/Edit/Copy", FALSE);
    set_menu_sensitivity("/Edit/Paste", FALSE);
#endif
    set_menus_for_captured_packets(FALSE);
    set_menus_for_selected_packet(FALSE);
    set_menus_for_selected_tree_row(FALSE);
  }
}

void
set_menu_sensitivity_meat(GtkItemFactory *ifactory, gchar *path, gint val) {
	GtkWidget *menu = NULL;
	
	if((menu = gtk_item_factory_get_widget(ifactory, path)) != NULL) {
		gtk_widget_set_sensitive(menu,val);
	}
}

/* Enable/disable menu sensitivity                       */
/* /menu/path            - old functionality             */
/* <MenuName>/menu/path  - new functionality             */
/* MenuName: <Main>, <PacketList>, <TreeView>, <HexDump> */
static void
set_menu_sensitivity (gchar *path, gint val) {
  GSList *menu_list = popup_menu_list;
  gchar *prefix;
  gchar *shortpath;

  if ('<' == *path) {
    /* New functionality => selective enable/disable per menu */
    prefix=strchr(path, '/');
    shortpath=strrchr(prefix, '/');

    if (0 == strncmp(path, "<Main>", 6))
      set_menu_sensitivity_meat(factory, prefix, val);
    else if (0 == strncmp(path, "<PacketList>", 12))
      set_menu_sensitivity_meat(packet_list_menu_factory, shortpath, val);
    else if (0 == strncmp(path, "<TreeView>", 10))
      set_menu_sensitivity_meat(tree_view_menu_factory, shortpath, val);
    else if (0 == strncmp(path, "<HexDump>", 9))
      set_menu_sensitivity_meat(hexdump_menu_factory, shortpath, val);
  } else {
    /* Old functionality => enable/disable all menus with same shortpath */
    shortpath = strrchr(path, '/');

    set_menu_sensitivity_meat(factory, path, val);

    while (menu_list != NULL) {
  	  set_menu_sensitivity_meat(menu_list->data, shortpath, val);
	  menu_list = g_slist_next(menu_list);
    }
  }
}

void
set_menu_object_data_meat(GtkItemFactory *ifactory, gchar *path, gchar *key, gpointer data)
{
	GtkWidget *menu = NULL;
	
	if ((menu = gtk_item_factory_get_widget(ifactory, path)) != NULL)
		gtk_object_set_data(GTK_OBJECT(menu), key, data);
}

void
set_menu_object_data (gchar *path, gchar *key, gpointer data) {
  GSList *menu_list = popup_menu_list;
  gchar *shortpath = strrchr(path, '/');
  
  set_menu_object_data_meat(factory, path, key, data);
  while (menu_list != NULL) {
  	set_menu_object_data_meat(menu_list->data, shortpath, key, data);
	menu_list = g_slist_next(menu_list);
  }
}

gint
popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	GtkWidget *menu = NULL;
	GdkEventButton *event_button = NULL;
	GtkCList *packet_list = NULL;
	gint row, column;

	if(widget == NULL || event == NULL || data == NULL) {
		return FALSE;
	}
	
	/*
	 * If we ever want to make the menu differ based on what row
	 * and/or column we're above, we'd use "gtk_clist_get_selection_info()"
	 * to find the row and column number for the coordinates; a CTree is,
	 * I guess, like a CList with one column(?) and the expander widget
	 * as a pixmap.
	 */
	/* Check if we are on packet_list object */
	if (widget == gtk_object_get_data(GTK_OBJECT(popup_menu_object),
		E_MPACKET_LIST_KEY)) {
	  packet_list=GTK_CLIST(widget);
	  if (gtk_clist_get_selection_info(GTK_CLIST(packet_list),
	      ((GdkEventButton *)event)->x,
	      ((GdkEventButton *)event)->y,&row,&column)) {
	    gtk_object_set_data(GTK_OBJECT(popup_menu_object),
		E_MPACKET_LIST_ROW_KEY, (gpointer *)row);
	    gtk_object_set_data(GTK_OBJECT(popup_menu_object),
		E_MPACKET_LIST_COL_KEY, (gpointer *)column);
	  }
	}
	menu = (GtkWidget *)data;
	if(event->type == GDK_BUTTON_PRESS) {
		event_button = (GdkEventButton *) event;
		
		if(event_button->button == 3) {
			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
			gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
			return TRUE;
		}
	}
	return FALSE;
}

/* Enable or disable menu items based on whether you have a capture file
   you've finished reading. */
void
set_menus_for_capture_file(gboolean have_capture_file)
{
  set_menu_sensitivity("/File/Open...", have_capture_file);
  set_menu_sensitivity("/File/Save As...", have_capture_file);
  set_menu_sensitivity("/File/Close", have_capture_file);
  set_menu_sensitivity("/File/Reload", have_capture_file);
}

/* Enable or disable menu items based on whether you have an unsaved
   capture file you've finished reading. */
void
set_menus_for_unsaved_capture_file(gboolean have_unsaved_capture_file)
{
  set_menu_sensitivity("/File/Save", have_unsaved_capture_file);
}

/* Enable or disable menu items based on whether there's a capture in
   progress. */
void
set_menus_for_capture_in_progress(gboolean capture_in_progress)
{
  set_menu_sensitivity("/File/Open...", !capture_in_progress);
  set_menu_sensitivity("/Capture/Start...", !capture_in_progress);
  /*
   * XXX - this doesn't yet work in Win32.
   */
#ifndef _WIN32
  set_menu_sensitivity("/Capture/Stop", capture_in_progress);
#endif
}

/* Enable or disable menu items based on whether you have some captured
   packets. */
void
set_menus_for_captured_packets(gboolean have_captured_packets)
{
  set_menu_sensitivity("/File/Print...", have_captured_packets);
  set_menu_sensitivity("/Edit/Find Frame...", have_captured_packets);
  set_menu_sensitivity("/Edit/Find Next", have_captured_packets);
  set_menu_sensitivity("/Edit/Find Previous", have_captured_packets);
  set_menu_sensitivity("/Edit/Go To Frame...", have_captured_packets);
  set_menu_sensitivity("/Display/Colorize Display...", have_captured_packets);
  set_menu_sensitivity("/Tools/Summary", have_captured_packets);
  set_menu_sensitivity("/Tools/Protocol Hierarchy Statistics", have_captured_packets);
  set_menu_sensitivity("<PacketList>/Display/Match", have_captured_packets);
  set_menu_sensitivity("<PacketList>/Display/Prepare", have_captured_packets);
}

/* Enable or disable menu items based on whether a packet is selected. */
void
set_menus_for_selected_packet(gboolean have_selected_packet)
{
  set_menu_sensitivity("/File/Print Packet", have_selected_packet);
  set_menu_sensitivity("/Edit/Mark Frame", have_selected_packet);
  set_menu_sensitivity("/Edit/Mark All Frames", have_selected_packet);
  set_menu_sensitivity("/Edit/Unmark All Frames", have_selected_packet);
  set_menu_sensitivity("/Display/Collapse All", have_selected_packet);
  set_menu_sensitivity("/Display/Expand All", have_selected_packet);
  set_menu_sensitivity("/Display/Show Packet In New Window", have_selected_packet);
  set_menu_sensitivity("/Tools/Follow TCP Stream",
      have_selected_packet ? (cfile.edt->pi.ipproto == 6) : FALSE);
  set_menu_sensitivity("/Tools/Decode As...",
      have_selected_packet && decode_as_ok());
  set_menu_sensitivity("/Resolve Name", 
      have_selected_packet && g_resolv_flags == 0);
  set_menu_sensitivity("/Tools/TCP Stream Analysis",
            have_selected_packet ? (cfile.edt->pi.ipproto == 6) : FALSE);
}

/* Enable or disable menu items based on whether a tree row is selected
   and and on whether a "Match" can be done. */
void
set_menus_for_selected_tree_row(gboolean have_selected_tree)
{
  gboolean properties = FALSE;

  if (finfo_selected) {
	header_field_info *hfinfo = finfo_selected->hfinfo;
	if (hfinfo->parent == -1) {
	  properties = prefs_is_registered_protocol(hfinfo->abbrev);
	} else {
	  properties = prefs_is_registered_protocol(proto_registrar_get_abbrev(hfinfo->parent));
	}
	set_menu_sensitivity("<Main>/Display/Match",
	  proto_can_match_selected(finfo_selected));
	set_menu_sensitivity("<TreeView>/Display/Match",
	  proto_can_match_selected(finfo_selected));
	set_menu_sensitivity("<Main>/Display/Prepare",
	  proto_can_match_selected(finfo_selected));
	set_menu_sensitivity("<TreeView>/Display/Prepare",
	  proto_can_match_selected(finfo_selected));
  } else {
	set_menu_sensitivity("<Main>/Display/Match", FALSE);
	set_menu_sensitivity("<TreeView>/Display/Match", FALSE);
	set_menu_sensitivity("<Main>/Display/Prepare", FALSE);
	set_menu_sensitivity("<TreeView>/Display/Prepare", FALSE);
  }

  set_menu_sensitivity("/Protocol Properties...", have_selected_tree && properties);
}
/*
 * addon that makes possible resending of a packet thas was previously captured 
 * sent_packet.c
 *
 * Copyright 2002, Miha Jemec <m.jemec@xxxxxxxxxxx> Iskratel, Ltd, Kranj
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


* to do:
 * defining of an interface: eth0, eth1, ... 
 */


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "globals.h"

#include <gtk/gtk.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include "simple_dialog.h"
#include "progress_dlg.h"
#include <sent_packet.h>

/* za pravice */
#include <unistd.h>
#include <sys/types.h>

/* za posiljanje paketa */
#include <net/if.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netdb.h>
/* end */

struct sockaddr_ll sa;
struct ifreq ifr;
char device[]="eth0";
char packet[1522];
int fd;
unsigned long time_lock = 0;
struct timeval now, t1;

/* this function is called when we want to send only one packet */
void sent_selected_cb(GtkWidget *w _U_) 
{
	int i, c, len;

	/* this the length of the frame */
	len = cfile.current_frame->cap_len;
	
	/* copy current frame data into packet[] */
	for(i=0; i<len; i++) {
	        sprintf(&packet[i], "%c", cfile.pd[i]);
	}
	
	/* open socket */
	if (open_socket() == -1) 
		return;
		
	/* sending packet */
	if ( (c = sendto(fd, packet, len, 0, (struct sockaddr *)&sa, sizeof (sa)) ) == -1) 
		simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with resending of the packet");
	else
		printf("there were %d bytes sent on the wire\n", c);
	
	/* closing socket... */
	if (close(fd) == -1)
		simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with closing socket!");
}


/* this function is called when we want to send all marked frames 
 * we send all the packets as fast as possible */
void sent_marked_cb(GtkWidget *w _U_) 
{
	sent_marked(0);
}
	
/* this function is called when we want to send all marked frames with 
 * preserve timing options on, that means we send the first
 * marked frame immediately, but try to send all the following in the 
 * same interval as they were captured */
void sent_marked_with_cb(GtkWidget *w _U_) 
{
	sent_marked(1);
}
	
void sent_marked(int with) 
{
	int i, len, c, err, j=1;
	frame_data *fdata;
	capture_file *cf;
	long time_gap = 0, get_gap = 0, diff_gap = 0;
	progdlg_t *progbar;
	guint32 progbar_count = 0, count = 0;
	gboolean stop_flag = FALSE;
	gchar tmp[30];
	
	cf = &cfile;
	
	/* are there any frames marked? */
	if (cfile.marked_count == 0) {
		simple_dialog(ESD_TYPE_CRIT, NULL, "No packets are marked");
		return;
	}
	
	/* open socket */
	if (open_socket() == -1) 
		return;
		
	/* number of frames to send */
 	progbar_count = cfile.marked_count;
	
	sprintf(tmp, "Sending %d packets", progbar_count);
	
	/* progress dialog so we can stop sending */
	progbar = create_progress_dlg(tmp, "Stop", &stop_flag);
   
	/* loop throught all marked frames */
	for (fdata = cf->plist; fdata != NULL; fdata = fdata->next) {
		if (fdata->flags.marked) {
			
			/* this the length of the frame */
			len = fdata->cap_len;
	
			/* load the current frame data into pd */
			wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header, 
					cf->pd, fdata->cap_len, &err);
		
			/* copy current frame data into packet[] */
			for(i=0; i<len; i++) {
		        sprintf(&packet[i], "%c", cf->pd[i]);
			}

			/* if we are in for the first time */
			if (j == 1) 
				j = 2;
			
			/* if not we calculate the gap difference */
			else {
				/* this is the gap in which packets were captured */
				time_gap = ( (fdata->rel_secs)*1000000 + (fdata->rel_usecs) ) - time_lock;
				
				if (gettimeofday(&now, NULL) == -1) {
					simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with gettimeofday");
					return;
				}
				
				/* this is the real gap */
				get_gap = (now.tv_sec*1000000+now.tv_usec)-(t1.tv_sec*1000000+t1.tv_usec);
			      	
				diff_gap = time_gap - get_gap;
			}
			
			
			/* wait or not to wait */
			if ( (with == 1) && (j == 2) && (diff_gap > 0) ) {
				/* ok, so we wait */
				while (time_gap > get_gap) {
					/* wait for one 1ms */
					usleep(1000);
					if (gettimeofday(&now, NULL) == -1) {
						simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with gettimeofday");
						return;
					}
					/* this is the new "real time" gap */
					get_gap = (now.tv_sec*1000000+now.tv_usec)-(t1.tv_sec*1000000+t1.tv_usec);
					/* if flag is on exit */
					if(stop_flag) 
						break;
					
				}
			}
			
			if (gettimeofday(&t1, NULL) == -1) {
				simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with gettimeofday");
				return;
			}
			time_lock = (fdata->rel_secs)*1000000 + (fdata->rel_usecs);
			

			/* sending packet */
			if ( (c = sendto(fd, packet, len, 0, (struct sockaddr *)&sa, sizeof (sa)) ) == -1) 
				simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with resending of the packet");
			else
				printf("there were %d bytes sent on the wire\n", c);
	
			/* update the progbar */
			count++;
	            	update_progress_dlg(progbar, (gfloat) count/progbar_count);

			if(stop_flag)
				break;
		}
	}	

	/* closing socket... */
	if (close(fd) == -1)
		simple_dialog(ESD_TYPE_CRIT, NULL, "Problems with closing socket!");

	/* closing progbar */
	destroy_progress_dlg(progbar);
}



/* go men go! */
int open_socket(void)
{
	
	/* do we have the rights to do that? */
	if (getuid() && geteuid()) {
		simple_dialog(ESD_TYPE_CRIT, NULL, "Sorry but need the su rights!\n");
		return -1;
	}
	
	/* open socket in raw mode */
	fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (fd == -1) {
		simple_dialog(ESD_TYPE_CRIT, NULL, "Socket error!");
		return -1;
	}

	/* which interface would you like to use? */
	memset(&ifr, 0, sizeof(ifr));
	strncpy (ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
	ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';

	if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
		simple_dialog(ESD_TYPE_CRIT, NULL, "No such device - ioctl error");
		close(fd);
		return -1;
	}	
	
	/* well we need this to work, don't ask me what is it about */
	memset(&sa, 0, sizeof (sa));
	sa.sll_family    = AF_PACKET;
	sa.sll_ifindex   = ifr.ifr_ifindex;
	sa.sll_protocol  = htons(ETH_P_ALL);

	return 1;
}



/*
 * sent_packet.h
 *
 * Declarations for sent_packet.h
 * Copyright 2002, Miha Jemec <m.jemec@xxxxxxxxxxx> Iskratel, Ltd, Kranj
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/


void sent_selected_cb(GtkWidget *w _U_);
void sent_marked_cb(GtkWidget *w _U_);
void sent_marked_with_cb(GtkWidget *w _U_);
void sent_marked(int);
int open_socket(void);
/*
 * This source code is a product of Sun Microsystems, Inc. and is provided
 * for unrestricted use.  Users may copy or modify this source code without
 * charge.
 *
 * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
 * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 * Sun source code is provided with no support and without any obligation on
 * the part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
 * OR ANY PART THEREOF.
 *
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*
 * g711.c
 *
 * u-law, A-law and linear PCM conversions.
 */
#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
#define	NSEGS		(8)		/* Number of A-law segments. */
#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
#define	SEG_MASK	(0x70)		/* Segment field mask. */

static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};

/* copy from CCITT G.711 specifications */
unsigned char _u2a[128] = {			/* u- to A-law conversions */
	1,	1,	2,	2,	3,	3,	4,	4,
	5,	5,	6,	6,	7,	7,	8,	8,
	9,	10,	11,	12,	13,	14,	15,	16,
	17,	18,	19,	20,	21,	22,	23,	24,
	25,	27,	29,	31,	33,	34,	35,	36,
	37,	38,	39,	40,	41,	42,	43,	44,
	46,	48,	49,	50,	51,	52,	53,	54,
	55,	56,	57,	58,	59,	60,	61,	62,
	64,	65,	66,	67,	68,	69,	70,	71,
	72,	73,	74,	75,	76,	77,	78,	79,
	81,	82,	83,	84,	85,	86,	87,	88,
	89,	90,	91,	92,	93,	94,	95,	96,
	97,	98,	99,	100,	101,	102,	103,	104,
	105,	106,	107,	108,	109,	110,	111,	112,
	113,	114,	115,	116,	117,	118,	119,	120,
	121,	122,	123,	124,	125,	126,	127,	128};

unsigned char _a2u[128] = {			/* A- to u-law conversions */
	1,	3,	5,	7,	9,	11,	13,	15,
	16,	17,	18,	19,	20,	21,	22,	23,
	24,	25,	26,	27,	28,	29,	30,	31,
	32,	32,	33,	33,	34,	34,	35,	35,
	36,	37,	38,	39,	40,	41,	42,	43,
	44,	45,	46,	47,	48,	48,	49,	49,
	50,	51,	52,	53,	54,	55,	56,	57,
	58,	59,	60,	61,	62,	63,	64,	64,
	65,	66,	67,	68,	69,	70,	71,	72,
	73,	74,	75,	76,	77,	78,	79,	79,
	80,	81,	82,	83,	84,	85,	86,	87,
	88,	89,	90,	91,	92,	93,	94,	95,
	96,	97,	98,	99,	100,	101,	102,	103,
	104,	105,	106,	107,	108,	109,	110,	111,
	112,	113,	114,	115,	116,	117,	118,	119,
	120,	121,	122,	123,	124,	125,	126,	127};

static int
search(
	int		val,
	short		*table,
	int		size)
{
	int		i;

	for (i = 0; i < size; i++) {
		if (val <= *table++)
			return (i);
	}
	return (size);
}

/*
 * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
 *
 * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
 *
 *		Linear Input Code	Compressed Code
 *	------------------------	---------------
 *	0000000wxyza			000wxyz
 *	0000001wxyza			001wxyz
 *	000001wxyzab			010wxyz
 *	00001wxyzabc			011wxyz
 *	0001wxyzabcd			100wxyz
 *	001wxyzabcde			101wxyz
 *	01wxyzabcdef			110wxyz
 *	1wxyzabcdefg			111wxyz
 *
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 * John Wiley & Sons, pps 98-111 and 472-476.
 */
unsigned char
linear2alaw(
	int		pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	aval;
	if (pcm_val >= 0) {
		mask = 0xD5;		/* sign (7th) bit = 1 */
	} else {
		mask = 0x55;		/* sign bit = 0 */
		pcm_val = -pcm_val - 8;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/* Combine the sign, segment, and quantization bits. */

	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		aval = seg << SEG_SHIFT;
		if (seg < 2)
			aval |= (pcm_val >> 4) & QUANT_MASK;
		else
			aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
		return (aval ^ mask);
	}
}

/*
 * alaw2linear() - Convert an A-law value to 16-bit linear PCM
 *
 */
int
alaw2linear(
	unsigned char	a_val)
{
	int		t;
	int		seg;
	//printf(" vrednost a_val %X ", a_val);
	a_val ^= 0x55;

	t = (a_val & QUANT_MASK) << 4;
	seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
	switch (seg) {
	case 0:
		t += 8;
		break;
	case 1:
		t += 0x108;
		break;
	default:
		t += 0x108;
		t <<= seg - 1;
	}
	//printf("izracunan int %d in njegov hex %X \n", t,t);
	return ((a_val & SIGN_BIT) ? t : -t);
}

#define	BIAS		(0x84)		/* Bias for linear code. */

/*
 * linear2ulaw() - Convert a linear PCM value to u-law
 *
 * In order to simplify the encoding process, the original linear magnitude
 * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
 * (33 - 8191). The result can be seen in the following encoding table:
 *
 *	Biased Linear Input Code	Compressed Code
 *	------------------------	---------------
 *	00000001wxyza			000wxyz
 *	0000001wxyzab			001wxyz
 *	000001wxyzabc			010wxyz
 *	00001wxyzabcd			011wxyz
 *	0001wxyzabcde			100wxyz
 *	001wxyzabcdef			101wxyz
 *	01wxyzabcdefg			110wxyz
 *	1wxyzabcdefgh			111wxyz
 *
 * Each biased linear code has a leading 1 which identifies the segment
 * number. The value of the segment number is equal to 7 minus the number
 * of leading 0's. The quantization interval is directly available as the
 * four bits wxyz.  * The trailing bits (a - h) are ignored.
 *
 * Ordinarily the complement of the resulting code word is used for
 * transmission, and so the code word is complemented before it is returned.
 *
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 * John Wiley & Sons, pps 98-111 and 472-476.
 */
unsigned char
linear2ulaw(
	int		pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	uval;

	/* Get the sign and the magnitude of the value. */
	if (pcm_val < 0) {
		pcm_val = BIAS - pcm_val;
		mask = 0x7F;
	} else {
		pcm_val += BIAS;
		mask = 0xFF;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/*
	 * Combine the sign, segment, quantization bits;
	 * and complement the code word.
	 */
	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
		return (uval ^ mask);
	}

}

/*
 * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
 *
 * First, a biased linear code is derived from the code word. An unbiased
 * output can then be obtained by subtracting 33 from the biased code.
 *
 * Note that this function expects to be passed the complement of the
 * original code word. This is in keeping with ISDN conventions.
 */
int
ulaw2linear(
	unsigned char	u_val)
{
	int		t;

	/* Complement to obtain normal u-law value. */
	u_val = ~u_val;

	/*
	 * Extract and bias the quantization bits. Then
	 * shift up by the segment number and subtract out the bias.
	 */
	t = ((u_val & QUANT_MASK) << 3) + BIAS;
	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;

	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}

/* A-law to u-law conversion */
unsigned char
alaw2ulaw(
	unsigned char	aval)
{
	aval &= 0xff;
	return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
	    (0x7F ^ _a2u[aval ^ 0x55]));
}

/* u-law to A-law conversion */
unsigned char
ulaw2alaw(
	unsigned char	uval)
{
	uval &= 0xff;
	return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
	    (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
}