/*
 * TCPVIEW
 *
 * Author:	Martin Hunt
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: martinh@cac.washington.edu
 *
 *
 * Copyright 1992 by the University of Washington
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
#ifndef lint
static char rcsid[] =
    "@(#) $Header: /usr/staff/martinh/tcpview/RCS/filter-dialog.c,v 1.2 1993/03/23 21:50:24 martinh Exp $ (UW)";
#endif

#include <stdio.h>
#include <string.h>
#include  <X11/Intrinsic.h>
#include  <X11/StringDefs.h>
#include  <Xm/Xm.h>
#include  <Xm/Frame.h>
#include  <Xm/MessageB.h>
#include  <Xm/RowColumn.h>
#include  <Xm/SeparatoG.h>
#include  <Xm/ToggleB.h>
#include  <Xm/BulletinB.h>
#include  <Xm/TextF.h>
#include  <Xm/SelectioB.h>

#include  "tcpview.h"
#include "filter.h"
#include "motif.h"
#include "addrtoname.h"

#define status(x)  ( (Filter.protocol & 1<<x) ? (True) : (False) )

#ifdef __STDC__
char *intoa(u_long);
void createpdmenu( Widget, char *, char **, int);
void update_fstr( struct filter *);
int GetEtherList( XmString *, int );
#else
char *intoa();
void createpdmenu();
void update_fstr();
int GetEtherList();
#endif

static Widget bb_widget, Pt[NUM_PROTOS];
static Widget name[4];  /* four names in address filter */
static Widget level[2], arrow[2], include[2], addr_and[2];
static Widget port_widget;
static char *arrowstr[] = { "-->","<->","<--" };
static char *levelstr[] = { "IP", "DLC" };
static char *incstr[] = { "EXCLUDE", "INCLUDE" };
static char *Andstr[] = { "OFF", "AND", "OR" };
static char *Onstr[] = { "OFF", "ON" };
static char filter_ipname[4][64], filter_dlcname[4][64];
static char portstring[64];
struct filter Filter;

void init_filter()
{
  int i;
  struct address_filter *af;
  extern struct timeval Timeout;
  Filter.protocol = 0;
  Filter.port = 0;
  strcpy(portstring,"ANY");
  for(i=0;i<2;i++) {
    af = &Filter.addr[i];
    af->level=1;
    strcpy(af->name1,"ANY");
    strcpy(af->name2,"ANY");
    af->address1[0] = 0;
    af->address2[0] = 0;
    af->direction = DIR_TO;
    af->include = 1;
    af->and = 0;
  }
  for(i=0;i<4;i++) {
    strcpy(filter_ipname[i],"ANY");
    strcpy(filter_dlcname[i],"ANY");
  }
  /* initialize timeout value */
  Timeout.tv_sec = 1;
  Timeout.tv_usec = 0;
}

static void clear_callback( widget, foo, call_data )
Widget  widget;
char    *foo;
caddr_t call_data;
{
  int i;

  init_filter();
  for(i=0;i<3;i++)
    SetLabel(name[i],"ANY");
  for(i=0;i<2;i++) {
    SetLabel(arrow[i],arrowstr[DIR_TO]);
    SetLabel(include[i],incstr[1]);
    SetLabel(level[i],levelstr[1]);
    SetLabel(addr_and[i],Onstr[0]);
  }
  for(i=1;i<7;i++) 
    SetToggle(Pt[i],False);
  SetToggle(Pt[0],True);
  SetLabel(port_widget,"ANY");
}       

static
void my_callback( widget, name, call_data )
Widget  widget;
char    *name;
caddr_t call_data;
{
  XtUnrealizeWidget( bb_widget ); 
}       /* my_callback */

static void current_callback( widget, name, call_data )
Widget  widget;
char    *name;
caddr_t call_data;
{
  extern void redisplay_current_list();

  XtUnrealizeWidget( bb_widget );
  update_fstr( &Filter );
  redisplay_current_list();
}       /* current_callback */


static void all_callback( widget, name, call_data )
Widget  widget;
char    *name;
caddr_t call_data;
{
  extern void redisplay_entire_list();

  /* turn off highlighting */
  HighlightTimeout = 0;

  XtUnrealizeWidget( bb_widget );
  update_fstr( &Filter );
  redisplay_entire_list();
}       /* all_callback */


static
void sel_callback( parent, data, cd )
Widget  parent;
caddr_t data;
XmSelectionBoxCallbackStruct *cd;
{
  char *string, *str, buf[64];
  int d;
  char *GetEtherFromName();

  d = (int)data>>1;
  XmStringGetLtoR( cd->value, XmSTRING_DEFAULT_CHARSET, &string); 
  str = string;
  if( (int)data & 1 ) {  /* address 2 */
    if(strcasecmp(string,"ALL") && strcasecmp(string,"ANY") && *string) {
      strcpy( Filter.addr[d].name2, string );
      if (Filter.addr[d].level == ADDR_DLC) {
	strcpy( Filter.addr[d].address2, GetEtherFromName(string) );
	if( Filter.addr[d].address2[0] == '\0' ) {
	  sprintf(buf,"DLC name %s not found",string);
	  ErrorDialog( parent, "ERROR", buf);
	}
      } else
	strcpy( Filter.addr[d].address2, string );
    } else {
      strcpy(Filter.addr[d].name2,"ANY");
      Filter.addr[d].address2[0] = '\0';
      str = "ANY";
    }
  } else {  /* address 1 */
    if( strcasecmp(string,"ALL") && strcasecmp(string,"ANY") && *string) {
      strcpy( Filter.addr[d].name1, string );
      if (Filter.addr[d].level == ADDR_DLC) {
	strcpy( Filter.addr[d].address1, GetEtherFromName(string) );
	if( Filter.addr[d].address1[0] == '\0' ) {
	  sprintf(buf,"DLC name %s not found",string);
	  ErrorDialog( parent, "ERROR", buf);
	}
      } else
	strcpy( Filter.addr[d].address1, string );
    } else {
      strcpy( Filter.addr[d].name1, "ANY" );
      Filter.addr[d].address1[0] = '\0';
      str = "ANY";
    }
  }
  SetLabel(name[(int)data], str);
  XtFree(string); 
}

static
void addr_name_callback( parent, data, call_data )
Widget  parent;
caddr_t data;
caddr_t call_data;
{
  Widget sel, list;
  Arg args[10];
  XmString list_items[512], current, label=0;
  int d = (int)data;
  int n, num=0;

  d = d>>1;
  n = (int)data & 1;
  if( n )
    current = XmStringCreateSimple(Filter.addr[d].name2);
  else
    current = XmStringCreateSimple(Filter.addr[d].name1);

  n=0;
  if (Filter.addr[d].level == ADDR_DLC) {
    num = GetEtherList( list_items, 512);
    label = XmStringCreateSimple("DLC Name List");
    XtSetArg( args[n], XmNlistLabelString, label ); n++;
    XtSetArg( args[n], XmNtextString, current ); n++;
    XtSetArg( args[n], XmNlistItems, list_items ); n++;
    XtSetArg( args[n], XmNlistItemCount, num ); n++;
    sel = XmCreateSelectionDialog(parent,"AddressSelect",args, n);
  } else {
    sel = XmCreatePromptDialog(parent,"AddressSelect",args,n);
  }

  XtAddCallback( sel, XmNokCallback, sel_callback, data); 
  list = XmSelectionBoxGetChild(sel,XmDIALOG_HELP_BUTTON);
  XtUnmanageChild(list);
  XtManageChild(sel);
  for(n=0;n<num;n++)
    XmStringFree(list_items[n]);
  if( label )
    XmStringFree(label);
  XmStringFree(current);
}       /* addr_name_callback */


static void port_sel_callback( parent, data, cd )
     Widget  parent;
     caddr_t data;
     XmSelectionBoxCallbackStruct *cd;
{
  char *string, *str, buf[64];
  char str1[64], str2[12];
  int port, proto;

  XmStringGetLtoR( cd->value, XmSTRING_DEFAULT_CHARSET, &string); 
  if( s_nametoport(string,&port,&proto) )
    Filter.port = port;
  else
    Filter.port = atoi(string);

  if( Filter.port ) {
    strcpy(portstring,tcpport_string(Filter.port));
    strcpy(str1,udpport_string(Filter.port));

    if( strcasecmp(portstring,str1) ) {
      strcat(portstring," / ");
      strcat(portstring,str1);
    }
    sprintf(str2,"%d",Filter.port);
    if( strcmp(portstring,str2) & strcmp(str1,str2) ) {
      strcat(portstring," (");
      strcat(portstring,str2);
      strcat(portstring,")");
    }
  } else
    strcpy(portstring,"ANY");
 
  SetLabel(port_widget, portstring);
  XtFree(string); 
}

static void port_name_callback( parent, data, call_data )
     Widget  parent;
     caddr_t data;
     caddr_t call_data;
{
  Widget sel, list;
  Arg args[2];
  int n=0;
  
  sel = XmCreatePromptDialog(parent,"AddressSelect",args,n);
  XtAddCallback( sel, XmNokCallback, port_sel_callback, data); 
  list = XmSelectionBoxGetChild(sel,XmDIALOG_HELP_BUTTON);
  XtUnmanageChild(list);
  XtManageChild(sel);
}       /* port_name_callback */

static
void addr_dir_callback( widget, data, call_data )
Widget  widget;
caddr_t data;
caddr_t call_data;
{
  int d = (int)data;
  if (Filter.addr[d].direction==2)
    Filter.addr[d].direction = 0;
  else
    Filter.addr[d].direction++;
  SetLabel(arrow[d],arrowstr[Filter.addr[d].direction]);
}       /* addr_dir_callback */

static
void addr_level_callback( widget, data, call_data )
Widget  widget;
caddr_t data;
caddr_t call_data;
{
  char *GetEtherFromName();
  int d = (int)data;

  if(Filter.addr[d].level==ADDR_DLC) {
    /* change from DLC to IP */
    Filter.addr[d].level = 0;
    strcpy( filter_dlcname[d<<1], Filter.addr[d].name1);
    strcpy( filter_dlcname[(d<<1)+1], Filter.addr[d].name2);
    strcpy( Filter.addr[d].name1, filter_ipname[d<<1] ); 
    strcpy( Filter.addr[d].name2, filter_ipname[(d<<1)+1] );
    if( !strcmp(Filter.addr[d].name1, "ANY") ) 
      Filter.addr[d].address1[0] = '\0';
    else
      strcpy( Filter.addr[d].address1, filter_ipname[d<<1] ); 
    if( !strcmp(Filter.addr[d].name2, "ANY") ) 
      Filter.addr[d].address2[0] = '\0';
    else
      strcpy( Filter.addr[d].address2, filter_ipname[(d<<1)+1] );    
  } else {  
    /* change from IP to to DLC */
    Filter.addr[d].level = ADDR_DLC;
    /* save ip names */
    strcpy( filter_ipname[d<<1], Filter.addr[d].name1);
    strcpy( filter_ipname[(d<<1)+1], Filter.addr[d].name2);
    strcpy( Filter.addr[d].name1, filter_dlcname[d<<1] ); 
    strcpy( Filter.addr[d].name2, filter_dlcname[(d<<1)+1] );
    strcpy( Filter.addr[d].address1, GetEtherFromName(Filter.addr[d].name1) ); 
    strcpy( Filter.addr[d].address2, GetEtherFromName(Filter.addr[d].name2) );
  }
  SetLabel(level[d],levelstr[Filter.addr[d].level]);
  SetLabel(name[(int)data<<1], Filter.addr[d].name1);
  SetLabel(name[((int)data<<1)+1], Filter.addr[d].name2);
}       /* addr_level_callback */

static
void addr_include_callback( widget, data, call_data )
Widget  widget;
caddr_t data;
caddr_t call_data;
{
  int d = (int)data;
  if(Filter.addr[d].include==0)
    Filter.addr[d].include = 1;
  else
    Filter.addr[d].include = 0;

  SetLabel(include[d],incstr[Filter.addr[d].include]);
}       /* addr_include_callback */

static
void addr_and_callback( widget, data, call_data )
Widget  widget;
caddr_t data;
caddr_t call_data;
{
  int d = (int)data;
  int i;
  int first = 1;

  /* first is 1 only if this is the first active toggle */
  for(i=0;i<d;i++)
    if( Filter.addr[i].and )
      first=0;

  if( first ) {
    /* only allowable states are "ON" and "OFF" */
    Filter.addr[d].and++;
    if(Filter.addr[d].and>1)
      Filter.addr[d].and = 0;
    SetLabel(addr_and[d],Onstr[Filter.addr[d].and]);
    if(d==0 && Filter.addr[1].and>0)
      if( Filter.addr[0].and )
	SetLabel(addr_and[1],Andstr[Filter.addr[1].and]);
      else {
	/* if the second line is 'OR' then change to 'ON' */
	if( Filter.addr[1].and==2 )
	  Filter.addr[1].and=1;
	SetLabel(addr_and[1],Onstr[Filter.addr[1].and]);
      }
  } else {
    /* allowable states are "AND", "OR"  and "OFF" */
    Filter.addr[d].and++;
    if(Filter.addr[d].and>2)
      Filter.addr[d].and = 0;
    SetLabel(addr_and[d],Andstr[Filter.addr[d].and]);
  }
}       /* addr_and_callback */

static
void proto_callback( widget, data, X_data )
Widget  widget;
caddr_t data;
XmToggleButtonCallbackStruct *X_data;
{
  int d = (int)data;

  /* if ALL then turn off others */
  if( d == PROTO_ALL ) {
    int i;
    Filter.protocol = 0;
    for(i=1;i<7;i++) 
      SetToggle(Pt[i],False);
    return;
  }
 
  if (X_data->set == True) {
    if( Filter.protocol == 0 )
      SetToggle(Pt[0],False);
    Filter.protocol |= 1<<d;
  } else
    if (Filter.protocol & 1<<d)
      Filter.protocol ^= 1<<d;
}  

void FilterDialog (parent, nam, callback)
Widget parent;
char *nam;
void (*callback)();
{
  Widget main_row, ok, current, cancel;
  Widget frame1, rc1, rc2;
  Arg args[10];
  int n, i, first;
  char *str;

  n=0;
  XtSetArg( args[n], XmNautoUnmanage, False ); n++;
  XtSetArg( args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL ); n++; 
  bb_widget = XmCreateBulletinBoardDialog (parent,"filter",args,n);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  main_row = XmCreateRowColumn( bb_widget,"main",args,n);
  XtManageChild( main_row ); 

  n=0;
  CreateLabelWidget (main_row, "addrfilter","ADDRESS FILTER",args,n);

  n=0;
  XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
  frame1 = XmCreateFrame (main_row,"frame",args,n);
  XtManageChild(frame1);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  rc1 = XmCreateRowColumn( frame1,"arow1",args,n);
  XtManageChild( rc1 ); 
  
  /* Now do both rows */
  first = 1;
  for (i=0;i<2;i++) {
    n=0;
    XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
    rc2 = XmCreateRowColumn( rc1,"arow2",args,n);
    XtManageChild( rc2 ); 

    if( first ) {
      addr_and[i] = CreateSimpleButton( rc2, Onstr[Filter.addr[i].and],
				       addr_and_callback, (caddr_t)i);
      if( Filter.addr[i].and ) first=0;
    } else 
      addr_and[i] = CreateSimpleButton( rc2, Andstr[Filter.addr[i].and],
				       addr_and_callback, (caddr_t)i);

    level[i] = CreateSimpleButton( rc2, levelstr[Filter.addr[i].level],
				addr_level_callback, (caddr_t)i);

    str = Filter.addr[i].name1;
    name[i+i] = CreateSimpleButton( rc2, str, addr_name_callback,(caddr_t)(i+i) );
    
    arrow[i] = CreateSimpleButton( rc2, arrowstr[Filter.addr[i].direction],
				addr_dir_callback, (caddr_t)i);

    str = Filter.addr[i].name2;
    name[i+i+1] = CreateSimpleButton( rc2, str, addr_name_callback, (caddr_t)(i+i+1) );
    include[i] = CreateSimpleButton( rc2, incstr[Filter.addr[i].include],
				    addr_include_callback, (caddr_t)i);
  }

  /********  PATTERN FILTER ***********/

#ifdef NOTDEF
  CreateLabelWidget (main_row, "patfilter","  PATTERN FILTER  ",args,0);

  n=0;
  XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
  frame1 = XmCreateFrame (main_row,"patframe",args,n);
  XtManageChild(frame1);

  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
  rc1 = XmCreateRowColumn( frame1,"patcol",args,n);
  XtManageChild( rc1 ); 
/*
  n = 0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rc2 = XmCreateRowColumn( rc1,"patrow",args,n);
  XtManageChild( rc2 ); 
*/

  /* These should be textfields */
  (void)CreateLabelWidget (rc1, "pattern1","00XX1500 at offset 3B  OR  ",args,0);
  (void)CreateLabelWidget (rc1, "pattern2","10XX1500 at offset 3B ",args,0);

#endif

  n=0;
  (void)CreateLabelWidget (main_row, "protofilter","PROTOCOL FILTER",args,n);

  /* make a frame */
  n=0;
  XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
  frame1 = XmCreateFrame (main_row,"frame",args,n);
  XtManageChild(frame1);

  /* create a row column to hold toggle buttons */
  n = 0;
  XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++; 
  XtSetArg( args[n], XmNnumColumns, 5 ); n++; 
  XtSetArg( args[n], XmNpacking, XmPACK_COLUMN ); n++;
  rc1 = XmCreateRowColumn(frame1,"protocols",args,n);

  Pt[PROTO_ALL] = CreateSimpleToggle (rc1, "ALL",Filter.protocol==PROTO_ALL,
		      XmONE_OF_MANY,proto_callback, (caddr_t)PROTO_ALL);
  Pt[PROTO_ARP] = CreateSimpleToggle (rc1, "ARP",status(PROTO_ARP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_ARP);
  Pt[PROTO_RARP] = CreateSimpleToggle (rc1, "RARP",status(PROTO_RARP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_RARP);
  Pt[PROTO_IP] = CreateSimpleToggle (rc1, "IP",status(PROTO_IP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_IP);
  Pt[PROTO_ICMP] = CreateSimpleToggle (rc1, "ICMP",status(PROTO_ICMP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_ICMP);
  Pt[PROTO_UDP] = CreateSimpleToggle (rc1, "UDP",status(PROTO_UDP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_UDP);
  Pt[PROTO_TCP] = CreateSimpleToggle (rc1, "TCP",status(PROTO_TCP),
		      XmN_OF_MANY,proto_callback, (caddr_t)PROTO_TCP);

  XtManageChild ( rc1 );

  /* port filter */
  n = 0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rc1 = XmCreateRowColumn( main_row,"port",args,n);
  n=0;
  (void)CreateLabelWidget (rc1, "","PORT ",args,n);
  port_widget = CreateSimpleButton( rc1, portstring, port_name_callback,(caddr_t)0 );
  XtManageChild(rc1);

  /* clear button */
  CreateSimpleButton(main_row,"CLEAR FILTER",clear_callback,(caddr_t)0);

  /***** now do buttons *****/
  n = 0;
  XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
  rc1 = XmCreateRowColumn( main_row,"buttons",args,n);

  ok = CreatePushButton( rc1, "all", "Apply To All" ,args, 0, all_callback );
  current = CreatePushButton( rc1, "current", "Apply to Current" ,args, 0, current_callback );
  cancel = CreatePushButton( rc1, "cancel", "Cancel" ,args, 0, my_callback );

  BulletinDefaultButton( bb_widget, current );
  XtManageChild( rc1 ); 

  XtManageChild( bb_widget );
}


static char *dirstr1[] = { "src ","host ","dst " };
static char *dirstr2[] = { "dst ","host ","src " };
static char *andstr[] = { "foo"," and "," or " };
static char *protofstr[] = { "foo","ether proto \\ip ", "ether proto \\arp ",
       "ether proto \\rarp ", "ip proto \\icmp ", "ip proto \\udp ", 
			      "ip proto \\tcp " };

char Fstr[256];

void update_fstr( filt )
struct filter *filt;
{
  int i, prev=0;
  u_long p;
  struct address_filter *f;

  Fstr[0] = '\0';
  for(i=0;i<2;i++) {
    f = &filt->addr[i];
    if( f->and && (*f->address1 || *f->address2) ) {
      if( prev++ && i > 0 ) 
	strcat(Fstr,andstr[f->and]);
      if( f->include == 0 )
	strcat(Fstr,"not ( ");
      if( *f->address1 ) {
	if( f->level )
	  strcat(Fstr,"ether ");
	strcat(Fstr,dirstr1[f->direction]);
	strcat(Fstr,f->address1);
	if( *f->address2)
	  strcat(Fstr," and ");
      }
      if( *f->address2 ) {
	if( f->level )
	  strcat(Fstr,"ether ");
	strcat(Fstr,dirstr2[f->direction]);
	strcat(Fstr,f->address2);
      }
      if( f->include == 0 )
	strcat(Fstr," )");
    } 
  }

  /* now for the protocols */
  prev = 0;
  p = filt->protocol;
  if( p != PROTO_ALL ) {
    if( *Fstr )
      strcat(Fstr," and ");
    strcat(Fstr,"( ");
    for(i=1;i<NUM_PROTOS;i++) {
      if( p & (1<<i) ) {
	if( prev++ ) strcat(Fstr,"or ");
	strcat(Fstr,protofstr[i]);
      }
    }
    strcat(Fstr,")");
  }

  /* and finally the port */
  if( filt->port ) {
    char buf[64];
    if( *Fstr) strcat(Fstr," and ");
    sprintf(buf,"port %d",filt->port);
    strcat(Fstr,buf);
  }
/*  fprintf(stderr,"Fstr=%s\n",Fstr);  */
}




syntax highlighted by Code2HTML, v. 0.9.1