/*
 *  sfrp.c -- simple freeload request protocol
 *  (surfer protocol)
 * 
 *  valid requests:
 *
 * mkvol  - make a temporary volume
 * rmvol - nuke a temporary volume
 * help [command] - duu...
 * quit - shoot quayle
 *
 */

#include "server.h"
#include "config.h"
#include "sfrp.h"
#include <syslog.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>

UserContext *uc_make();
UserContext *fd_to_uc();
extern int do_timer_stuff_flag;

#define OK 0
#define EOL 1
#define SOCKCLOSED 2
#define OVERFLOW 3
#define ERROR 4 /* other errors */


typedef enum { 
  NOTACOM = -2,
  NOINPUT,
  MKVOL = 0,
  RMVOL,
  HELP,
  STATUS,
  DUMP,
  QUIT,
  MAXCOMMANDS
}  ComTypes;

static char *commands[] = {
  "mkvol",
  "rmvol",
  "help",
  "status",
  "dump",
  "quit"
  };
   

static int sf_sock;
fd_set fdset;

sfrp_init()
{

  sf_sock = so_open(PORT_NUMBER);
  FD_ZERO(&fdset);
  FD_SET(sf_sock, &fdset);
}

sfrp_rehash_fd()
{
  FD_ZERO(&fdset);
  FD_SET(sf_sock, &fdset);
  uc_rehash_fd(&fdset);
}

int sfrp_handle()
{
  int nfound;
  fd_set rfdset;  
  UserContext *uc;
  
  rfdset = fdset;

  if (do_timer_stuff_flag)
    do_timer_stuff();
  
  nfound = select(FD_SETSIZE, &rfdset, 0, 0, 0);
  
  if(nfound == -1)  {
    if(errno == EINTR) do_timer_stuff();
    else sfrp_rehash_fd(); 
    return 0;
  }  else if(FD_ISSET(sf_sock, &rfdset))  {
    int s;
    struct sockaddr_in name;
    int namelen = sizeof(name);
     
    s = so_next_dude(sf_sock);
    uc = uc_make();
    uc->sock = s;
    getpeername (s, &name, &namelen);
    syslog (LOG_DEBUG, "Received new connection on fd %d from %s", s, inet_ntoa(name.sin_addr));
    FD_SET(s, &fdset);
    sfrp_hello(uc);
    initialize_user(uc);

  } else {
    int retval;

    uc = fd_to_uc(&rfdset);
    retval = get_input(uc);
    
    switch(retval) {
    case SOCKCLOSED:
      close_connection(uc);
      break; 

    case ERROR:
      break;

    case OK:
      break;

    case EOL:
      sfrp_handle_command(uc);
      break;
    }
  }
  return 1; /* true = handled something */
}

close_connection(uc)
     UserContext *uc;
{
      FD_CLR(uc->sock, &fdset);
      close(uc->sock);
      uc_free(uc);
}

/* 
  reads in to context input and returns codes:
  OK
  EOL
  SOCKCLOSED
  OVERFLOW   (there's more than 2k of stuff in one command!)
  ERROR 
*/

  
static int get_input(uc)
     UserContext *uc;
{
  int retval;
  int i;

  i = read(uc->sock, &(uc->input[uc->indx]), 1);
    

  switch(i) {
  case 0:
    return SOCKCLOSED;
    
  case -1:
    syslog(LOG_ALERT, "read: %m"); 
    return ERROR; 
    
  case 1:
    if(uc->input[uc->indx] == '\n') {
      uc->input[uc->indx] = 0;
      return EOL;  /* this should then flow to sfrp_handle_command() */
    } else {
      uc->indx++;
      return OK;
    }
  }

  
  if(uc->indx = MAXINPUTSIZE) 
    return OVERFLOW;

}

int sfrp_output(uc,str, condition,  more)
     UserContext *uc;
     char *str;
     int condition, more;
{
  char buf[10];
  sprintf(buf, "%03d%c", condition, more? '-' :' ');
  write(uc->sock, buf, 4);
  write(uc->sock, str, strlen(str));  
  write(uc->sock, "\n", strlen("\n"));
  return 1;
}


/* tokenizes the command and puts cur_input at the first argument (or at \0 if no args) */
static ComTypes parse_input(uc) 
     UserContext *uc;
{
  char str[100];
  char *sptr = str;
  int i;
  char *cur_input;

  for(cur_input = uc->input; isspace(*cur_input) ; cur_input++) 
    if(!*cur_input) return NOINPUT;
  
  while(*cur_input && !isspace(*cur_input)) {
    if(isupper(*cur_input)) 
      *sptr = tolower(*cur_input);
    else
      *sptr = *cur_input;

    cur_input++;
    sptr++;
  }

  *sptr = 0;

  for(cur_input = uc->input; *cur_input && isspace(*cur_input) ; cur_input++);
  uc->indx = cur_input - uc->input;

  for(i = 0 ; i < (int)MAXCOMMANDS; i++ ) {
    if(strcmp(str, commands[i]) == 0 ) {
      syslog (LOG_DEBUG, "User %s executing command %s\n",
	      uc->name, commands[i]);
      return (ComTypes)i;
    }
  }
  
  return NOTACOM;
}

static void notacom(uc)
     UserContext *uc;
{
  sfrp_output(uc, "ERROR:  Not a command.  Use 'help' for command info.", ERROUT, 0);
}


sfrp_handle_command(uc)
     UserContext *uc;
{
  ComTypes com;

  com = parse_input(uc);
  
  switch(com) {
  case NOTACOM:
    notacom(uc);
    break;
    
  case NOINPUT:
    break;

  case MKVOL:
    com_mkvol(uc);
    break;

  case RMVOL:
    com_rmvol(uc);
    break;

  case HELP:
    com_help(uc);
    break;
    
  case STATUS:
    com_status(uc);
    break;

  case DUMP:
    com_dump(uc);
    
  case QUIT:
    com_quit(uc);
    return; /* don't break to uc->indx = 0 */
    
  }
  
  uc->indx = 0;
}


sfrp_hello(uc)
     UserContext *uc;
{
  sfrp_output(uc, "Yo dude! what's up?", CONNECTEDOUT, 0);
}
