/* Nessus
 * Copyright (C) 1998 Renaud Deraison
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <includes.h>
#include "parser.h"
#include <gtk/gtk.h>
#include "xstuff.h"
#include "auth.h"
#include "error_dialog.h"
#include "globals.h"

static void add_port(struct arglist * , char *, int,  char *);
/*
 * parse_message_type
 *
 * This functions performs the conversion
 * char * --> type
 */
int 
parse_message_type(type)
	char * type;
{
#ifdef DEBUGMORE
 fprintf(stderr, "%s:%d type : %s\n", __FILE__, __LINE__, type);
#endif
 if(!strcmp(MSG_HOLE_STR, type))return(MSG_HOLE);
 if(!strcmp(MSG_INFO_STR, type))return(MSG_INFO);
 if(!strcmp(MSG_STAT_STR, type))return(MSG_STAT);
 if(!strcmp(MSG_PORT_STR, type))return(MSG_PORT);
 if(!strcmp(MSG_ERROR_STR, type))return(MSG_ERROR);
 if(!strcmp(MSG_PING_STR, type))return(MSG_PING);
 if(!strcmp(MSG_PLUGINS_ORDER_STR, type))return(MSG_PLUGINS_ORDER);
 if(!strcmp(MSG_BYE_STR, type))return(MSG_BYE);
 return(-1);
}

/*
 * parse_server_message
 *
 * This function analyzes a message received
 * from the server, and performs what must
 * must be done depending on the server message
 */
int 
parse_server_message(message, hosts, humanmsg)
	char * message;
	struct arglist * hosts;
	char * humanmsg;
{
 char * message_type;
 char * buf;
 char * t;
 
 t = strstr(message, "SERVER <|> ");
 if(!t)return(-1);
 buf = strstr(message, "<|>");
 buf+=4;
 t = strstr(buf, " <|>");
 if(t)
 {
 t[0]=0;
 }
 message_type = emalloc(strlen(buf)+1);
 strncpy(message_type,buf, strlen(buf));
 if(t)t[0]=' ';
 switch(parse_message_type(message_type))
 {
  case MSG_ERROR :
        {
        if(!F_quiet_mode)
          {
           char * msg = parse_separator(t);
           show_error(msg);
  	   return(MSG_ERROR);
  	  }
  	break;
  	}
  case MSG_PORT :
  	parse_host_add_port(hosts, t, humanmsg);
  	return(MSG_PORT);
  	break;
  case MSG_HOLE :
     	humanmsg[0]=0;
  	parse_host_add_data(hosts, t, MSG_HOLE);
  	return(MSG_HOLE);
  	break;
  case MSG_INFO :
  	humanmsg[0]=0;
  	parse_host_add_data(hosts, t, MSG_INFO);
  	break;
  case MSG_STAT :
  	strncpy(humanmsg, buf+strlen(message_type), strlen(buf+strlen(message_type)));
  	return(MSG_STAT);
  	break;
  case MSG_PING :
  	humanmsg[0]=0;
  	network_printf("CLIENT <|> PONG <|> PING <|> CLIENT\n\n");
  	return(MSG_PING);
  	break;
  case MSG_PLUGINS_ORDER :
        {
        char * t = strstr(buf, " <|> SERVER");
        if(t)t[0]=0;
        strncpy(humanmsg, buf+strlen(message_type)+5, strlen(buf+strlen(message_type)));
        return(MSG_PLUGINS_ORDER);
        }
        break;
  case MSG_BYE :
        humanmsg[0]=0;
  	return(MSG_BYE);
  	break;
  }
 efree(&message_type);
 return(-1);
}

/*
 * The server has sent a STATUS message.
 *
 */
void 
parse_nessusd_status(servmsg, host, action, current, max)
	char * servmsg;
	char * host;
        char * action;
	int  * current;
	int * max;
{
	char * t1,*t2;
	
	t1 = parse_separator(servmsg);if(!t1)return;
	t2 = parse_separator(servmsg+strlen(t1)+3);
	if(!t2)return;
	
	strncpy(host, t1, strlen(t1));
        strncpy(action, t2, strlen(t2));
        
        t2 = parse_separator(servmsg+strlen(t1)+strlen(t2)+3);
	t1 = strchr(t2, '/');
	t1[0]=0;
	*current = atoi(t2);
	t1+=sizeof(char);
	*max = atoi(t1);
}

/*
 * The server has sent a PORT message
 *
 */
void 
parse_host_add_port(hosts, servmsg, humanmsg)
	struct arglist * hosts;
	char * servmsg;
	char * humanmsg;
{
 char * port;
 char * hostname;
 char * t1, * t2;
 struct arglist * host;
 
 t1 = parse_separator(servmsg);if(!t1)return;
 t2 = parse_separator(servmsg+strlen(t1)+3);
 
#ifdef DEBUGMORE
 fprintf(stderr, "%s:%d t1: %s\n", __FILE__, __LINE__, t1);
#endif
 hostname = emalloc(strlen(t1)+1);
 strncpy(hostname, t1, strlen(t1));
 efree(&t1);
 
#ifdef DEBUGMORE
 fprintf(stderr, "%s:%d t2: %s\n", __FILE__, __LINE__, t2);
#endif

 port = emalloc(strlen(t2)+1);
 strncpy(port, t2, strlen(t2));
 efree(&t2);
 
 host = arg_get_value(hosts, hostname);
 if(!host)
 {
  host = emalloc(sizeof(struct arglist));
  arg_add_value(hosts, hostname, ARG_ARGLIST, -1, host);
 }
 
  add_port(host, port, 1, "");
#ifdef DEBUGMORE
  fprintf(stderr, "Port %s opened on %s\n", port, hostname);
#endif

  sprintf(humanmsg, "Port %s opened on %s", port, hostname);
  efree(&hostname);
  efree(&port);
}

/* 
 * The server has sent a HOLE or INFOS message
 */
void 
parse_host_add_data(hosts, servmsg, type)
	struct arglist * hosts;
	char * servmsg;
	int type;
{
 char * port;
 char * data;
 char * hostname;
 char * t1, * t2, *t3;
 struct arglist * host;

 t1 = parse_separator(servmsg);if(!t1)return;
 t2 = parse_separator(servmsg+strlen(t1)+3);if(!t2){
 	      efree(&t1);
 		return;
 		}
 
 hostname = emalloc(strlen(t1)+1);
 strncpy(hostname, t1, strlen(t1));
 efree(&t1);
 
 port = emalloc(strlen(t2)+1);
 strncpy(port, t2, strlen(t2));

 
 t3 = parse_separator(servmsg+strlen(hostname)+strlen(t2)+3);

 
 if(!t3)return;
 data = emalloc(strlen(t3)+1);
 strncpy(data, t3, strlen(t3));
 efree(&t2);
 while((t2=strchr(data, ';')))t2[0]='\n';
 
 host = arg_get_value(hosts, hostname);
 if(!host)
 {
  host = emalloc(sizeof(struct arglist));
  arg_add_value(hosts, hostname, ARG_ARGLIST, -1, host);
 }
 
  switch(type)
  {
   case MSG_HOLE :
   l_post_hole(host, port, data);
   break;
   case MSG_INFO :
   l_post_info(host, port, data);
   break;
   default :
   fprintf(stderr, "received unknown message type (%d)\n", type);
   break;
  }
#ifdef DEBUGMORE
  fprintf(stderr,"data for %s (port %d) [type : %d] : \n%s\n", hostname, port, type,data);
#endif
 
  efree(&hostname);
  efree(&data);
}
 

/* 
 * This function extracts the string after the 
 * ' <|> ' symbol.
 *
 */
char * 
parse_symbol(str)
	char * str;
{
 char * s = str;
 
 while(s)
 {
#ifdef DEBUGMORE
 fprintf(stderr, "%s:%d s : %s\n", __FILE__, __LINE__, s);
#endif
 s = strchr(s, '|');
 if(!s)return(NULL);
 if((s[1]=='>')&&(s-1)[0]=='<')
 	{
#ifdef DEBUGMORE
 	fprintf(stderr, "returning \"%s\"\n", s+3);
#endif
 	return(s+3);
 	}
 s++;
 }
 return(NULL);
}

char * parse_separator(str)
	char * str;
{
 char * s_1, *s_2;
 char * ret;
 
 s_1 = parse_symbol(str);
 if(!s_1)return(NULL);
 s_2 = parse_symbol(s_1);
 if(!s_2)
  {
  ret = emalloc(strlen(s_1));
  strncpy(ret, s_1, strlen(s_1)-1);
  }
 else
 {
 int c;
 s_2 = s_2 - 4;
 c = s_2[0];
 s_2[0] = 0;
 ret = emalloc(strlen(s_1));
 strncpy(ret, s_1, strlen(s_1)-1);
 s_2[0]=c;
 }
#ifdef DEBUGMORE
 fprintf(stderr, "%s:%d got %s returning \"%s\"\n", __FILE__, __LINE__, str, ret);
#endif
  return(ret);
}
 
 
void 
add_port(hostdata, portnum, state, banner)
 struct arglist * hostdata;
 char * portnum;
 int state;
 char * banner;
{
 struct arglist * port ;
 struct arglist * portlist;
 char * l_banner = emalloc(banner ? strlen(banner)+1:0);

 if(banner)strncpy(l_banner, banner, strlen(banner));
 port = emalloc(sizeof(struct arglist));
 if(hostdata)
 	 portlist = arg_get_value(hostdata, "PORTS");
 else return;
 if(!portlist)
 {
  portlist = emalloc(sizeof(struct arglist));
  arg_add_value(hostdata, "PORTS", ARG_ARGLIST, sizeof(portlist), portlist);

 }
 arg_add_value(port, "STATE", ARG_INT, sizeof(int), (void *)state);
 arg_add_value(port, "BANNER", ARG_STRING, banner ? strlen(l_banner):0, l_banner);
 arg_add_value(portlist, portnum, ARG_ARGLIST, sizeof(port), port);
}

 
