#include "ipxtund.h"

static u_int str_to_frametype(const char *str);
static int read_mac_str(u_char *mac, const char *str);
static int splitstring(char *splits[], char *str, const int maxsplits);
static void lowercases(char *str);

int read_config_file(struct Config *config, const char *path)
{
  int k, n, p, splits_anz, line_no;
  u_int ft, frametypes;
  u_short port;
  char line[MAXLINE], str[MAXLINE], *splits[10];
  FILE *fp;
  
  if ((fp=fopen(path, "r")) == NULL) {
    errormsg(E_ALL,"read_config_file","fopen %s: %m", path);
    return -1;
  }

  *config->local.hostid = '\0';
  *config->local.server_name = '\0';
  config->ether_anz = 0;
  config->router_anz = 0;
  config->local.udp_port = DEFAULT_UDP_PORT;
  config->local.ctrl_port = DEFAULT_CTRL_PORT;
  config->local.tcp.tcp_listen_port = DEFAULT_TCP_PORT;
  frametypes = 0;

  line_no = 0;
  while (fgets(line, MAXLINE, fp)) {
    line_no++;
    if (*line == '#' || isspace(*line) || *line == '\n') {
      continue;
    }
    strcpy(str, line);
    lowercases(str);
    k = 0;
    while (str[k] != '#' && str[k] != '\0') {
      k++;
    }
    str[k] = '\0';
    splits_anz = splitstring(splits, str, 10);
    
    if (!strncmp(splits[0], "server", 8)) {
      if (splits_anz != 3) {
	errormsg(E_ALL,"read_config_file",
            "line %d:wrong syntax, localhost id IP-address expected",
		  line_no);
	return -1;
      }
      strcpy(config->local.server_name, splits[1]);
      strcpy(config->local.hostid, splits[2]);
    }
    else if (!strncmp(splits[0], "port", 4)) {
      p = 0;
      for (k = 1; k < splits_anz; k++) {
	port = atoi(splits[k]);
	switch (p++) {
	case 0:
	  config->local.udp_port = port;
	  break;
	case 1:
	  config->local.ctrl_port = port;
	  break;
	case 2:
	  config->local.tcp.tcp_listen_port = port;
	  break;
	default:
	  errormsg(E_ALL,"read_config_file",
		    "line %d:too many port numbers", line_no);
	  return -1;
	}
      }
    }
    else if (!strncmp(splits[0], "router", 6)) {
      n = config->router_anz++;
      if (n > MAX_ROUTERS - 1) {
        errormsg(E_ALL,"read_config_file",
	         "too many routers in configuration");
	return -1;
      }
      strcpy(config->router[n].name, splits[1]);
      config->router[n].udp_port = DEFAULT_UDP_PORT;
      config->router[n].ctrl_port = DEFAULT_CTRL_PORT;
      config->router[n].tcp_port = DEFAULT_TCP_PORT;
      config->router[n].s_frametype = DEFAULT_UDP_FRAMETYPE;
      p = 0;
      for (k = 2; k < splits_anz; k++) {
        if (*splits[k] == '(') {
	  ft = str_to_frametype(splits[k]);
	  if (ft == 0) {
  	    errormsg(E_ALL,"read_config_file",
	             "line %d: wrong frametype syntax", line_no);
	    return -1;
	  }
	  else if (ft != ETHER_NONE && ft != ETHER_TRANS) {
  	    errormsg(E_ALL,"read_config_file",
	             "line %d: wrong frametype (only n or t are allowed)",
		     line_no);
	    return -1;
	  }
	  config->router[n].s_frametype = ft;
	}
	else {
	  port = atoi(splits[k]);
	  switch (p++) {
	  case 0:
	    config->router[n].udp_port = port;
	    break;
	  case 1:
	    config->router[n].ctrl_port = port;
	    break;
	  case 2:
	    config->router[n].tcp_port = port;
	    break;
	  default:
	    errormsg(E_ALL,"read_config_file",
		      "line %d:too many port numbers", line_no);
	    return -1;
	  }
	}
      }
    }
    else if (!strncmp(splits[0], "interface", 9)) {
      n = config->ether_anz++;
      if (n > MAX_ETHERS - 1) {
        errormsg(E_ALL,"read_config_file",
	         "too many interfaces in configutation");
	return -1;
      }
      strcpy(config->ether[n].name, splits[1]);
      memset((void *)config->ether[n].mac, '\0', 6);
      config->ether[n].s_frametype = DEFAULT_ETHER_FRAMETYPE;
      for (k = 2; k < splits_anz; k++) {
        if (*splits[k] == '(') {
	  ft = str_to_frametype(splits[k]);
	  if (ft == 0) {
  	    errormsg(E_ALL,"read_config_file",
	             "line %d: wrong frametype syntax", line_no);
	    return -1;
	  }
          config->ether[n].s_frametype = ft;
          if (ft == ETHER_AUTO) {
            config->ether[n].s_autoframetype = TRUE;
          }
          else {
            config->ether[n].s_autoframetype = FALSE;
          }
	}
	else if (*splits[k] == '[') {
	  if (read_mac_str(config->ether[n].mac, splits[k]) < 0) {
  	    errormsg(E_ALL,"read_config_file",
	           "line %d: wrong MAC syntax", line_no);
            return -1;
	  }
	}
      }
    }
    else if (!strncmp(splits[0], "frametype", 5)) {
      ft = str_to_frametype(splits[1]);
      if (ft == 0) {
	errormsg(E_ALL,"read_config_file",
		  "line %d: wrong frametype syntax", line_no);
	return -1;
      }
      if (ft == ETHER_TRANS) {
	errormsg(E_ALL,"read_config_file",
		  "line %d: frametype (t) not allowed", line_no);
	return -1;
      }
      if (ft == ETHER_AUTO) {
	errormsg(E_ALL,"read_config_file",
		  "line %d: frametype (a) not allowed", line_no);
	return -1;
      }
      frametypes |= ft;
    }
    else {
      errormsg(E_ALL,"read_config_file",
	         "garbage in line no %d: %s", line_no, line);
      return -1;
    }
  }
  if (frametypes == 0) {
    frametypes = DEFAULT_ETHER_FRAMETYPE;
  }
  
  config->local.r_frametypes = frametypes;
  for (k = 0; k < config->ether_anz; k++) {
    config->ether[k].r_frametypes = frametypes;
  }

  fclose(fp);
  return 0;
}


static u_int str_to_frametype(const char *str)
{
  if (!strcmp(str, "(ii)")) {
    return ETHER_II;
  }
  else if (!strcmp(str, "(802.2)")) {      
    return ETHER_802_2;
  }
  else if (!strcmp(str, "(802.3)")) {
    return ETHER_802_3;
  }
  else if (!strcmp(str, "(snap)")) {
    return ETHER_SNAP;
  }
  else if (!strcmp(str, "(t)")) {
    return ETHER_TRANS;
  }
  else if (!strcmp(str, "(a)")) {
    return ETHER_AUTO;
  }
  else if (!strcmp(str, "(n)")) {
    return ETHER_NONE;
  }
  return 0;
}


static int read_mac_str(u_char *mac, const char *str)
{
  int k;
  u_int uc;
  const u_char *cp;
  
  cp = str;
  if (*cp != '[') {
    return -1;
  }
  for (k=0; k < 6; k++) {
    cp++;
    sscanf(cp,"%2x", (u_int *) &uc);
    while (*cp != ':' && *cp != ']' && *cp) {
      cp++;
    }
    mac[k] = uc;
  }
  if (*cp != ']') {
    return -1;
  }
  return 0;
}


static int splitstring(char *splits[], char *str, const int maxsplits)
{
  int k, n;
  char *p;
  
  p = str;
  k = 0;
  while (*p!='\0' && k<maxsplits) {
    while (*p==' ' || *p=='\t' || *p=='\n') p++;
    splits[k++] = p;
    while (*p!=' ' && *p!='\t' && *p!='\n' && *p!='\0') p++;
    if (*p!='\0') {
      *p = '\0';
      p++;
    }
  }
  n = k;
  while (k < maxsplits)  splits[k++] = "";
  return n;
}


static void lowercases(char *str)
{
  while (*str != '\0') {
    *str = tolower(*str);
    str++;
  }
}
