/***************************************
  $Revision: 1.21 $

  Protocol config module (pc).  This is the protocol that the admin uses to
  talk to the server.

  Status: NOT REVUED, NOT TESTED

  ******************/ /******************
  Filename            : protocol_config.c
  Authors             : ottrey@ripe.net
                        marek@ripe.net
  To Do               : Add a facility to take callbacks instead of
                        hard-coding menu options.
                        Add in all the menu support provided by the GLib
                        libraries.
                        (Remove strtok if multiple threads are to be used.)
			use gnu readline with expansion and history
  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  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 appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR 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, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/
#include <stdio.h>
#include <stdlib.h>
/*** solaris' header file doesn't contain the crypt definition...
     #include <unistd.h> */

extern char* crypt(const char *, const char *);   /* crypt stuff */
#include <time.h>       /* Time stuff */
#include <sys/ioctl.h>  /* Terminal control stuff */
#include <termio.h>     /* Terminal control stuff */

#include "mysql_driver.h"
#include "constants.h"
#include "properties.h"
#include "thread.h"
#include "protocol_config.h"
#include "access_control.h"
#include "socket.h"
#include "ta.h"

/*+ Each command has a +*/
typedef struct _command {
  const char *name;                          /*+ Name to be invoked. +*/
  char *(*function)(char *, sk_conn_st *);   /*+ Function to be invoked. +*/
  const char *help;                                /*+ Command help. +*/
} Command;

/*
 * Forward declarations
 */
static char *command_help(char *input, sk_conn_st *condat);
static char *command_quit(char *input, sk_conn_st *condat);
static char *command_show(char *input, sk_conn_st *condat);
static char *command_repeat(char *input, sk_conn_st *condat);
static char *show_const(char *input, sk_conn_st *condat);
static char *show_consts(char *input, sk_conn_st *condat);
static char *show_props(char *input, sk_conn_st *condat);
static char *show_threads(char *input, sk_conn_st *condat);
static char *show_whois(char *input, sk_conn_st *condat);
static char *show_access(char *input, sk_conn_st *condat);
static char *show_acl(char *input, sk_conn_st *condat);
static char *command_set(char *input, sk_conn_st *condat);
static char *set_const(char *input, sk_conn_st *condat);
static char *set_consts(char *input, sk_conn_st *condat);
static char *set_props(char *input, sk_conn_st *condat);
static char *command_sql(char *input, sk_conn_st *condat);
static char *set_ban(char *input, sk_conn_st *condat);

/*+
 * Contains the command definitions
+*/
static struct _command command[] = {
  {"help"   , command_help   , HELP_HELP   },
  {"quit"   , command_quit   , HELP_QUIT   },
  {"show"   , command_show   , HELP_SHOW   },
  {"repeat" , command_repeat , HELP_REPEAT },
  {"set"    , command_set    , HELP_SET    },
  {"sql"    , command_sql    , HELP_SQL    },
  {NULL     , NULL           , NULL        }
};

/*+
 * Contains the show commands
+*/
static struct _command show[] = {
  {"const"   , show_const   , HELP_SHOW_CONST   },
  {"consts"  , show_consts  , HELP_SHOW_CONSTS  },
  {"props"   , show_props   , HELP_SHOW_PROPS   },
  {"threads"  , show_threads  , HELP_SHOW_THREAD  },
  {"whois"   , show_whois   , HELP_SHOW_WHOIS   },
  {"access"  , show_access  , HELP_SHOW_ACCESS  },
  {"acl"     , show_acl     , HELP_SHOW_ACL     },
  {NULL      , NULL         , NULL              }
};

/*+
 * Contains the set commands
+*/
static struct _command set[] = {
  {"const"  , set_const  , HELP_SET_CONST  },
  {"consts" , set_consts , HELP_SET_CONSTS },
  {"props"  , set_props  , HELP_SET_PROPS  },
  {"ban"    , set_ban    , HELP_SET_BAN    },
  {NULL     , NULL       , NULL            }
};

static int find_command(char *comm_name, Command *comm) {
  int i, index;
  char comm_buffer[STR_L];

  if (comm_name != NULL) {
    strcpy(comm_buffer, comm_name);
    strtok(comm_buffer, " \t");
    for (i=0, index=-1; comm[i].name != NULL; i++) {
      if ( strcmp(comm_buffer, comm[i].name) == 0) {
        index = i;
        break;
      }
    }
  }
  else {
    index = -2;
  }

  return index;
} /* find_command() */

static char *show_commands(Command *comm) {
  char *str;
  char help_buffer[STR_XL];
  char help_comm[STR_M];
  int i;

  sprintf(help_buffer, " commands are:\n\n");
  i = 0;
  while (comm[i].name != NULL) {
    sprintf(help_comm, "%s\t%s\n", comm[i].name, comm[i].help);
    strcat(help_buffer, help_comm);
    i++;
  }

  /* str = (char *)calloc(1, strlen(help_buffer)+1); */
  dieif( wr_malloc((void **)&str, strlen(help_buffer)+1) != UT_OK);  
  strcpy(str, help_buffer);

  return str;
} /* show_commands() */


/*
 * Command functions
 */
static char *command_help(char *input, sk_conn_st *condat) {
  char *str;
  char *str1;
  char output_buffer[STR_XXL];
  char *command_name;
  int index;

  strcpy(output_buffer, "");

  strtok(input, " \t");
  command_name = (char *)strtok(NULL, " \t");

  index = find_command(command_name, command);

  switch (index) {
    case -2:
      strcat(output_buffer, "Main");
      str1 = show_commands(command);
      strcat(output_buffer, str1);
      wr_free(str1);
      break;

    case -1:
      strcat(output_buffer, HELP_ERROR);
      strcat(output_buffer, command_name);
      break;

    default: 
      strcat(output_buffer, command[index].help);
  }

  /*
  str = (char *)CopyString(output_buffer);
  */
  /*  str = (char *)calloc(1, strlen(output_buffer)+1); */
  dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);  
  strcpy(str, output_buffer);

  return str;
} /* command_help() */

static char *command_quit(char *input, sk_conn_st *condat) {
    /* Administrator wishes to quit. */
  return NULL;
} /* command_quit() */

static char *show_const(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show constants. */
  char *result;
  char *name;
  char *tmp_input;

  /* tmp_input = (char *)calloc(1, strlen(input)+1); */
  dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);  
  strcpy(tmp_input, input);

  /* The name will be the third token in stuff */
  strtok(tmp_input, " ");
  strtok(NULL, " ");
  name = (char *)strtok(NULL, " ");

  result = CO_const_to_string(name);

  wr_free(tmp_input);
  return result;

} /* show_const() */

static char *show_consts(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show constants. */
  return CO_to_string();

} /* show_consts() */

static char *show_props(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show properties. */
  return PR_to_string();

} /* show_props() */

static char *show_threads(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show thread information. */
  return TA_tostring();

} /* show_thread() */

static char *show_whois(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show whois query information. */
  return wr_string("WQ_to_string();");

} /* show_whois() */

static char *show_access(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show whois query information. */
  
  char line[128];
  int cnt;
  er_ret_t err; 

  if( act_runtime->top_ptr != NULL ) {
    char *header = AC_to_string_header();
    
    /* print header */
    SK_cd_puts(condat,header);
    wr_free(header);

    cnt = rx_walk_tree(act_runtime->top_ptr, AC_rxwalkhook_print, 
		       RX_WALK_SKPGLU,  /* print no glue nodes */
		       255, 0, 0, condat, &err);
    sprintf(line,"Found %d nodes\n", cnt);
    SK_cd_puts(condat,line);
  }
  
  return wr_string("");

} /* show_access() */

static char *show_acl(char *input, sk_conn_st *condat) {
  /* Administrator wishes to show access control list. */
  
  char line[128];
  int cnt;
  er_ret_t err; 

  if( act_acl->top_ptr != NULL ) {
    char *header = AC_acl_to_string_header();
    
    /* print header */
    SK_cd_puts(condat,header);
    wr_free(header);

    cnt = rx_walk_tree(act_acl->top_ptr, AC_rxwalkhook_print_acl, 
		       RX_WALK_SKPGLU,  /* print no glue nodes */
		       255, 0, 0, condat, &err);
    sprintf(line,"Found %d nodes\n", cnt);
    SK_cd_puts(condat,line);
  }
  
  return wr_string("");

} /* show_acl() */

static char *command_execute(char *input, char *comm_name, 
			     Command *comm, sk_conn_st *condat) {
  char *str;
  char *str1;
  char output_buffer[STR_XXL];
  char *name;
  int index;
  char *tmp_input;

  /* Make a copy of the input */
  /* tmp_input = (char *)calloc(1, strlen(input)+1); */
  dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);  
  strcpy(tmp_input, input);

  strtok(tmp_input, " \t");
  name = (char *)strtok(NULL, " \t");

  index = find_command(name, comm);

  switch (index) {
    case -2:
      str1 = show_commands(comm);
      sprintf(output_buffer, "%s%s", comm_name, str1);
      wr_free(str1);
      break;

    case -1:
      sprintf(output_buffer, "%s invalid command: %s", comm_name, name);
      break;

    default: 
      sprintf(output_buffer, "%s", comm[index].function(input, condat));
  }

  /*
  str = (char *)CopyString(output_buffer);
  */
  /* str = (char *)calloc(1, strlen(output_buffer)+1); */
  dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);  
  strcpy(str, output_buffer);

  wr_free(tmp_input);

  return str;
} /* command_execute() */

static char *command_show(char *input, sk_conn_st *condat) {
  return command_execute(input, "Show", show, condat);
} /* command_show() */

static char *command_repeat(char *input, sk_conn_st *condat) {
  char *command_ptr;

  /* Goto the bit after "repeat n " */
  for (command_ptr=input+7; command_ptr[0] != ' ' || (command_ptr[0] >= '0' && command_ptr[0] <= '9'); command_ptr++);

  return command_ptr+1;

} /* command_show() */

static char *set_const(char *input, sk_conn_st *condat) {
  /* Administrator wishes to set a constant. */
  char *result;
  char result_buf[STR_M];
  char *tmp_input;
  char *name;
  char *value;
  int value_len;
  char *stuff;
  char *str;

  /* tmp_input = (char *)calloc(1, strlen(input)+1); */
  dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);  
  strcpy(tmp_input, input);

  stuff = (char *)strtok(tmp_input, "=");

  /* The value will be after the '=' */
  value = (char *)strtok(NULL, "=");

  /* The name will be the third token in stuff */
  strtok(stuff, " ");
  strtok(NULL, " ");
  name = (char *)strtok(NULL, " ");

  /* Remove any quotes */
  if (value[0] == '"') {
    value++;
  }
  value_len=strlen(value);
  if (value[value_len-1] == '"') {
    value[value_len-1]='\0';
  }

  printf("set_const name=(%s), value=(%s)\n", name, value);
  if (CO_set_const(name, value) == 0) {
    strcpy(result_buf, "Constant successfully set\n");
  }
  else {
    str = CO_const_to_string(name);
    sprintf(result_buf, "Constant not successfully set\nReverting to:   %s=%s\n", name, str);
    wr_free(str);
  }

  /* result = (char *)calloc(1, strlen(result_buf)+1); */
  dieif( wr_malloc((void **)&result, strlen(result_buf)+1) != UT_OK);  
  strcpy(result, result_buf);

  wr_free(tmp_input);
  return result;
} /* set_const() */

static char *set_consts(char *input, sk_conn_st *condat) {
  /* Administrator wishes to set constants. */
  return CO_set();
} /* set_consts() */

static char *set_props(char *input, sk_conn_st *condat) {
  /* Administrator wishes to set properties. */
  return PR_set();
} /* set_props() */

static char *set_ban(char *input, sk_conn_st *condat) {
  int flag;
  char addrstr[128];

  if( sscanf(input,"set ban %d %127s", &flag, addrstr) < 2) {
    return wr_string("Invalid arguments");
  }
  else {
    if( ! NOERR( AC_asc_ban_set( addrstr, "Manual", (flag!=0) ))) {
      return wr_string("Error\n");
    }
    else {
      return wr_string("OK");
    }
  }
}

static char *command_set(char *input, sk_conn_st *condat) {
  return command_execute(input, "Set", set, condat);
} /* command_set() */

static char *command_sql(char *input, sk_conn_st *condat) {
  char *str;
  char output_buffer[STR_XXL];
  char *sql_str;

  char *res=NULL;

  SQ_result_set_t *sql_result=NULL;
  SQ_connection_t *sql_connection;

  sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), CO_get_database(), CO_get_user(), CO_get_password() );

  if (sql_connection == NULL) {
    printf("/* Check for errors */\n");
  }

  /* skip over the "sql" */
  sql_str = input+3;
  /* skip over white space */
  while (sql_str[0] == ' ') {
    sql_str++;
  }

  strcpy(output_buffer, "");

  if (sql_connection != NULL) {
    if (strcmp(sql_str, "status") == 0) {
      /* Get the status of the database */
      res = SQ_info_to_string(sql_connection);
    }
    else {
      if (strcmp(sql_str, "") == 0) {
        /* Execute the default query (from the properties file) */
	  SQ_execute_query(sql_connection, CO_get_query(), &sql_result);
      }
      else {
        /* Execute an sql query */
	  SQ_execute_query(sql_connection, sql_str, &sql_result);
      }
      if (sql_result != NULL) {
        res = SQ_result_to_string(sql_result);
      }
      else {
        printf("no results\n");
      }
    }
    if (res != NULL) {
      sprintf(output_buffer, "%s", res);
    }
    else {
      printf("empty results\n");
    }
  }
  else {
    printf("Failed to make connection\n");
  }

  /*
  strcat(output_buffer, mysql_info(sql_connection));
  */

  strcat(output_buffer, "XXX Results from mysql_info(sql_connection) is meant to go here.  But it's not working!");

  /*
  str = (char *)CopyString(output_buffer);
  */
  /* str = (char *)calloc(1, strlen(output_buffer)+1); */
  dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);  
  strcpy(str, output_buffer);

  wr_free(res);
  SQ_free_result(sql_result);

  SQ_close_connection(sql_connection);

  return str;

} /* command_sql() */


/* process_input() */
/*++++++++++++++++++++++++++++++++++++++

  Process the input.

  sk_conn_st *condat         connection data    

  More:
  +html+ <PRE>
  Author:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
static int process_input(char *input, sk_conn_st *condat) {
  int connected = 1;
  char *input_ptr;
  char *output;
  int  index;
  int repeat=0;

  input_ptr = input;

  if (strncmp(input, "repeat", 6) == 0) {
    /* XXX This is a really dodgy call, that hopefully converts
    the string to the value of the first found integer. */
    repeat = atoi(input+7);
    input_ptr= command_repeat(input, condat);
  }

  index = find_command(input_ptr, command);

  do {
    switch (index) {
      case -1:
        /* Command not found */
        output = command_help(NULL, condat);
        break;

      default: 
        output = command[index].function(input_ptr, condat);
    }

    if(output == NULL) {
      connected = 0;
    } else {
      /*
      printf("thread output=\n%s\n", output);
      */
      if ( CO_get_clear_screen() == 1 ) {
        SK_cd_puts(condat, CLEAR_SCREEN);
      }
      SK_cd_puts(condat,  output);
      SK_cd_puts(condat, "\n");
      SK_cd_puts(condat, CO_get_prompt());
      
      wr_free(output);
    }

    if (repeat > 0) {
      repeat--;
      sleep(CO_get_sleep_time());
    }

  } while (repeat > 0);

  return connected;

} /* process_input() */

static void log_config(const char *user, const char *status) {
  FILE *logf;
  time_t now;
  char timebuf[26];

  time(&now);

  if (CO_get_config_logging() == 1) {
    
    if (strcmp(CO_get_config_logfile(), "stdout") == 0) {
      printf(LOG_CONFIG, TH_get_id(), user, status, ctime_r(&now, timebuf));
    }
    else {
      logf = fopen(CO_get_config_logfile(), "a");
      fprintf(logf, LOG_CONFIG, TH_get_id(), user, status, ctime_r(&now, timebuf));
      fclose(logf);
    }
  }

} /* log_config() */
 
/* XXX Doh! These only change the server's terminal.  We need some
   tricky escape sequence to send over the socket.
static void echo_off(int sock) {
  struct termio state;

  ioctl(0, TIOCGETP, &state);
  state.c_lflag &= ~ECHO;
  ioctl(0, TIOCSETP, &state);
} echo_off() */

/* XXX Doh! These only change the server's terminal.  We need some
   tricky escape sequence to send over the socket.
static void echo_on(int sock) {
  struct termio state;

  ioctl(0, TIOCGETP, &state);
  state.c_lflag |= ECHO;
  ioctl(0, TIOCSETP, &state);
} echo_on() */

static char *authenticate_user(sk_conn_st *condat) {
  char *user = NULL;
  const char Salt[2] = "DB";
  char input[MAX_INPUT_SIZE];
  int read_result;
  char *password=NULL;
  char *user_password=NULL;
  char user_buf[10];

  SK_cd_puts(condat, LOGIN_PROMPT);
  read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);

  strncpy(user_buf, input, 10);

  SK_cd_puts(condat, PASSWD_PROMPT);
  /* XXX These aren't working.
  SK_puts(sock, ECHO_ON);
  echo_off(sock);
  */
  read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
  /* XXX These aren't working.
  echo_on(sock);
  SK_puts(sock, ECHO_OFF);
  */

  password = crypt(input, Salt);

  user_password = PR_get_property(user_buf, DEFAULT_USER_NAME);

  if (user_password != NULL) {
    if (strcmp(password, user_password) == 0) {
      /*user = (char *)calloc(1, strlen(user_buf)+1);*/
      dieif( wr_malloc((void **)&user, strlen(user_buf)+1) != UT_OK);  
      strcpy(user, user_buf);
    }
  }

  if (user == NULL) {
    log_config(user_buf, "unsuccesful login attempt");
  }

  return user;

} /* authenticate_user() */

void PC_interact(int sock) {
  char input[MAX_INPUT_SIZE];
  int connected = 1;
  char *user=NULL;
  sk_conn_st condat;

  memset( &condat, 0, sizeof(condat));
  condat.sock = sock;
  SK_getpeerip(sock, &(condat.rIP));
  condat.ip = SK_getpeername(sock); /* XXX *alloc involved */
  
  /* Welcome the client */
  SK_cd_puts(&condat, CO_get_welcome());

  /* Authenticate the user */
  if (CO_get_authenticate() == 1) {
    user = authenticate_user(&condat);
  }
  else {
    user="nobody";
  }

  if (user != NULL) {
    

    /* Log admin logging on */
    log_config(user, "logged on");

    
    {
      char timestring[26];
      extern time_t SV_starttime;
      
      ctime_r(&SV_starttime, timestring); 
      SK_cd_printf(&condat, 
		   "System running since %sUptime in seconds: %ld \n\n",
		   timestring,		  
		   time(NULL) - SV_starttime);
    }
    
    SK_cd_puts(&condat, CO_get_prompt());

    while (condat.rtc==0 && connected) {
      char *ichr;
      char *icopy;
      char *chr;
      /* Read input */
      SK_cd_gets(&condat, input, MAX_INPUT_SIZE);

      /* filter junk out: leading/trailing whitespaces */
      
      /* 1. advance to non-whitespace */
      for(ichr=input; *ichr != 0 && isspace(*ichr); ichr++) {
	/* EMPTY */
      }
      /* 2. copy the rest (even if empty) */
      dieif( (icopy = strdup(ichr)) == NULL);
      
      /* 3. chop trailing spaces */
      for( chr = icopy + strlen(icopy)-1 ; 
	   chr != icopy && isspace(*chr);
	   chr--) {
	*chr = 0;
      }
      
      /* set thread accounting */
      TA_setactivity(icopy);
      TA_increment();
      
      connected = process_input(icopy, &condat);
      
      TA_setactivity("");
      
      free(icopy);
    }
    
    /* Log admin logging off */
    log_config(user, "logged off");
  }
  
  /* Close the socket */
  SK_close(sock);

  wr_free(condat.ip);
} /* PC_interact() */

