/*

  serverconfig.c

  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>

  Copyright (C) 1997 - 2000 Pekka Riikonen

  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.

*/
/* $Id: serverconfig.c,v 1.26 2001/11/14 14:44:27 priikone Exp $ */

#include "serverincludes.h"
#include "server_internal.h"

SilcServerConfigSection silc_server_config_sections[] = {
  { "[Cipher]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
  { "[PKCS]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 1 },
  { "[Hash]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
  { "[hmac]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
  { "[ServerKeys]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS, 2 },
  { "[ServerInfo]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
  { "[AdminInfo]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO, 4 },
  { "[ListenPort]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT, 3 },
  { "[Identity]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY, 2 },
  { "[Logging]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
  { "[ConnectionClass]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS, 4 },
  { "[ClientConnection]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION, 5 },
  { "[ServerConnection]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
  { "[RouterConnection]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 7 },
  { "[AdminConnection]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION, 5 },
  { "[DenyConnection]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 3 },
  { "[motd]", 
    SILC_CONFIG_SERVER_SECTION_TYPE_MOTD, 1 },
  { "[pid]",
    SILC_CONFIG_SERVER_SECTION_TYPE_PID, 1},
  
  { NULL, SILC_CONFIG_SERVER_SECTION_TYPE_NONE, 0 }
};

/* Allocates a new configuration object, opens configuration file and
   parses the file. The parsed data is returned to the newly allocated
   configuration object. */

SilcServerConfig silc_server_config_alloc(char *filename)
{
  SilcServerConfig new;
  SilcBuffer buffer;
  SilcServerConfigParse config_parse;

  SILC_LOG_DEBUG(("Allocating new configuration object"));

  new = silc_calloc(1, sizeof(*new));
  if (!new) {
    fprintf(stderr, "Could not allocate new configuration object");
    return NULL;
  }

  new->filename = filename;

  SILC_LOG_DEBUG(("Loading config data from `%s'", filename));

  /* Open configuration file and parse it */
  config_parse = NULL;
  buffer = NULL;
  silc_config_open(filename, &buffer);
  if (!buffer)
    goto fail;
  if ((silc_server_config_parse(new, buffer, &config_parse)) == FALSE)
    goto fail;
  if ((silc_server_config_parse_lines(new, config_parse)) == FALSE)
    goto fail;

  silc_free(buffer);

  return new;

 fail:
  silc_free(new);
  return NULL;
}

/* Free's a configuration object. */

void silc_server_config_free(SilcServerConfig config)
{
  if (config) {
    silc_free(config->filename);
    silc_free(config->server_keys);
    silc_free(config->server_info);
    silc_free(config->admin_info);
    silc_free(config->listen_port);
    silc_free(config->identity);
    silc_free(config->conn_class);
    silc_free(config->clients);
    silc_free(config->admins);
    silc_free(config->servers);
    silc_free(config->routers);
    silc_free(config->denied);
    silc_free(config->motd);
    silc_free(config->pidfile);
    silc_free(config);
  }
}

/* Parses the the buffer and returns the parsed lines into return_config
   argument. The return_config argument doesn't have to be initialized 
   before calling this. It will be initialized during the parsing. The
   buffer sent as argument can be safely free'd after this function has
   succesfully returned. */

int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer, 
			     SilcServerConfigParse *return_config)
{
  int i, begin, linenum;
  char line[1024], *cp;
  SilcServerConfigSection *cptr = NULL;
  SilcServerConfigParse parse = *return_config, first = NULL;

  SILC_LOG_DEBUG(("Parsing configuration file"));

  begin = 0;
  linenum = 0;
  while((begin = silc_gets(line, sizeof(line), 
			   buffer->data, buffer->len, begin)) != EOF) {
    cp = line;
    linenum++;

    /* Check for bad line */
    if (silc_check_line(cp))
      continue;

    /* Remove tabs and whitespaces from the line */
    if (strchr(cp, '\t')) {
      i = 0;
      while(strchr(cp + i, '\t')) {
	*strchr(cp + i, '\t') = ' ';
	i++;
      }
    }
    for (i = 0; i < strlen(cp); i++) {
      if (cp[i] != ' ') {
	if (i)
	  cp++;
	break;
      }
      cp++;
    }

    /* Parse line */
    switch(cp[0]) {
    case '[':
      /*
       * Start of a section
       */

      /* Remove new line sign */
      if (strchr(cp, '\n'))
	*strchr(cp, '\n') = '\0';
      
      /* Check for matching sections */
      for (cptr = silc_server_config_sections; cptr->section; cptr++)
	if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
	  break;

      if (!cptr->section) {
	fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
			config->filename, linenum, cp);
	return FALSE;
      }

      break;
    default:
      /*
       * Start of a configuration line
       */

      if (cptr->type != SILC_CONFIG_SERVER_SECTION_TYPE_NONE) {
	
	if (strchr(cp, '\n'))
	    *strchr(cp, '\n') = ':';

	if (parse == NULL) {
	  parse = silc_calloc(1, sizeof(*parse));
	  parse->line = NULL;
	  parse->section = NULL;
	  parse->next = NULL;
	  parse->prev = NULL;
	} else {
	  if (parse->next == NULL) {
	    parse->next = silc_calloc(1, sizeof(*parse->next));
	    parse->next->line = NULL;
	    parse->next->section = NULL;
	    parse->next->next = NULL;
	    parse->next->prev = parse;
	    parse = parse->next;
	  }
	}
	
	if (first == NULL)
	  first = parse;

	/* Add the line to parsing structure for further parsing. */
	if (parse) {
	  parse->section = cptr;
	  parse->line = silc_buffer_alloc(strlen(cp) + 1);
	  parse->linenum = linenum;
	  silc_buffer_pull_tail(parse->line, strlen(cp));
	  silc_buffer_put(parse->line, cp, strlen(cp));
	}
      }
      break;
    }
  }
  
  /* Set the return_config argument to its first value so that further
     parsing can be started from the first line. */
  *return_config = first;

  return TRUE;
}

/* Parses the lines earlier read from configuration file. The config object
   must not be initialized, it will be initialized in this function. The
   parse_config argument is uninitialized automatically during this
   function. */

int silc_server_config_parse_lines(SilcServerConfig config, 
				   SilcServerConfigParse parse_config)
{
  int ret, check = FALSE;
  uint32 checkmask;
  char *tmp;
  SilcServerConfigParse pc = parse_config;
  SilcBuffer line;

  SILC_LOG_DEBUG(("Parsing configuration lines"));
  
  if (!config)
    return FALSE;
  
  checkmask = 0;
  while(pc) {
    check = FALSE;
    line = pc->line;

    /* Get number of tokens in line */
    ret = silc_config_check_num_token(line);
    if (ret < pc->section->maxfields) {
      /* Bad line */
      fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
	      config->filename, pc->linenum, ret, 
	      pc->section->maxfields);
      break;
    }

    /* Parse the line */
    switch(pc->section->type) {
    case SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->cipher);

      /* Get cipher name */
      ret = silc_config_get_token(line, &config->cipher->alg_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Cipher name not defined\n",
		config->filename, pc->linenum);
	break;
      }

      /* Get module name */
      config->cipher->sim_name = NULL;
      ret = silc_config_get_token(line, &config->cipher->sim_name);
      if (ret < 0)
	break;

      /* Get key length */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Cipher key length not defined\n",
		config->filename, pc->linenum);
	break;
      }
      config->cipher->key_len = atoi(tmp);
      silc_free(tmp);

      /* Get block length */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Cipher block length not defined\n",
		config->filename, pc->linenum);
	break;
      }
      config->cipher->block_len = atoi(tmp);
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_PKCS:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->pkcs);

      /* Get PKCS name */
      ret = silc_config_get_token(line, &config->pkcs->alg_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: PKCS name not defined\n",
		config->filename, pc->linenum);
	break;
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->hash_func);

      /* Get Hash function name */
      ret = silc_config_get_token(line, &config->hash_func->alg_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Hash function name not defined\n",
		config->filename, pc->linenum);
	break;
      }
      
      /* Get Hash function module name */
      config->hash_func->sim_name = NULL;
      ret = silc_config_get_token(line, &config->hash_func->sim_name);
      if (ret < 0)
	break;

      /* Get block length */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Hash function block length not defined\n",
		config->filename, pc->linenum);
	break;
      }
      config->hash_func->block_len = atoi(tmp);
      silc_free(tmp);

      /* Get hash length */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
		config->filename, pc->linenum);
	break;
      }
      config->hash_func->key_len = atoi(tmp);
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);

      /* Get HMAC name */
      ret = silc_config_get_token(line, &config->hmac->alg_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: HMAC name not defined\n",
		config->filename, pc->linenum);
	break;
      }

      /* Get hash name */
      ret = silc_config_get_token(line, &config->hmac->sim_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Hash function name not defined\n",
		config->filename, pc->linenum);
	break;
      }
      
      /* Get MAC length */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
		config->filename, pc->linenum);
	break;
      }
      config->hmac->key_len = atoi(tmp);
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS:

      if (!config->server_keys)
	config->server_keys = silc_calloc(1, sizeof(*config->server_keys));

      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Public key name not defined\n",
		config->filename, pc->linenum);
	break;
      }
      
      if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
				     SILC_PKCS_FILE_PEM))
	if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
				       SILC_PKCS_FILE_BIN)) {
	  fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}
      silc_free(tmp);

      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Private key name not defined\n",
		config->filename, pc->linenum);
	break;
      }
      
      if (!silc_pkcs_load_private_key(tmp, &config->server_keys->private_key, 
				     SILC_PKCS_FILE_BIN))
	if (!silc_pkcs_load_private_key(tmp, 
					&config->server_keys->private_key, 
					SILC_PKCS_FILE_PEM)) {
	  fprintf(stderr, "%s:%d: Could not load private key file `%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:

      if (!config->server_info)
	config->server_info = silc_calloc(1, sizeof(*config->server_info));

      /* Get server name */
      ret = silc_config_get_token(line, &config->server_info->server_name);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Server name not defined */

      }
      
      /* Get server IP */
      ret = silc_config_get_token(line, &config->server_info->server_ip);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Server IP not defined */

      }

      /* Get server location */
      ret = silc_config_get_token(line, &config->server_info->location);
      if (ret < 0)
	break;

      /* Get server port */
      /* XXX: Need port here??? */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Port not defined */

      }
      config->server_info->port = atoi(tmp);
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO:

      if (!config->admin_info)
	config->admin_info = silc_calloc(1, sizeof(*config->admin_info));

      /* Get location */
      ret = silc_config_get_token(line, &config->admin_info->location);
      if (ret < 0)
	break;

      /* Get server type */
      ret = silc_config_get_token(line, &config->admin_info->server_type);
      if (ret < 0)
	break;

      /* Get admins name */
      ret = silc_config_get_token(line, &config->admin_info->admin_name);
      if (ret < 0)
	break;

      /* Get admins email address */
      ret = silc_config_get_token(line, &config->admin_info->admin_email);
      if (ret < 0)
	break;

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);

      /* Get local IP */
      ret = silc_config_get_token(line, &config->listen_port->local_ip);
      if (ret < 0)
	break;

      /* Get listener IP */
      ret = silc_config_get_token(line, &config->listen_port->listener_ip);
      if (ret < 0)
	break;

      /* Get port */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Any port */
	config->listen_port->port = 0;
      } else {
	config->listen_port->port = atoi(tmp);
	silc_free(tmp);
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY:

      if (!config->identity)
        config->identity = silc_calloc(1, sizeof(*config->identity));

      /* Get user */
      ret = silc_config_get_token(line, &config->identity->user);
      if (ret < 0)
        break;
      /* Get group */
      ret = silc_config_get_token(line, &config->identity->group);
      if (ret < 0)
        break;

      check = TRUE;
      checkmask |= (1L << pc->section->type);

    case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);

      /* Get class number */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Class number not defined */

      }
      config->conn_class->class = atoi(tmp);
      silc_free(tmp);

      /* Get ping frequency */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      config->conn_class->ping_freq = atoi(tmp);
      silc_free(tmp);

      /* Get connect frequency */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      config->conn_class->connect_freq = atoi(tmp);
      silc_free(tmp);

      /* Get max links */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      config->conn_class->max_links = atoi(tmp);
      silc_free(tmp);

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->logging);

      /* Get log section type and check it */
      ret = silc_config_get_token(line, &config->logging->logtype);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Log file section not defined\n", 
		config->filename, pc->linenum);
	break;
      }
      if (strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_INFO)
	  && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_WARNING)
	  && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_ERROR)
	  && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
	fprintf(stderr, "%s:%d: Unknown log file section '%s'\n",
		config->filename, pc->linenum, config->logging->logtype);
	break;
      }

      /* Get log filename */
      ret = silc_config_get_token(line, &config->logging->filename);
      if (ret < 0)
	break;
      if (ret == 0) {
	fprintf(stderr, "%s:%d: Log file name not defined\n",
		config->filename, pc->linenum);
	break;
      }

      /* Get max byte size */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->logging->maxsize = atoi(tmp);
	silc_free(tmp);
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->clients);

      /* Get host */
      ret = silc_config_get_token(line, &config->clients->host);
      if (ret < 0)
	break;
      if (ret == 0)
	/* Any host */
	config->clients->host = strdup("*");

      /* Get authentication method */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
	    strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
	  fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
	  config->clients->auth_meth = SILC_AUTH_PASSWORD;

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
	  config->clients->auth_meth = SILC_AUTH_PUBLIC_KEY;

	silc_free(tmp);
      }

      /* Get authentication data */
      ret = silc_config_get_token(line, (char **)&config->clients->auth_data);
      if (ret < 0)
	break;

      if (config->clients->auth_meth == SILC_AUTH_PASSWORD) {
	config->clients->auth_data_len = strlen(config->clients->auth_data);
      } else if (config->clients->auth_meth == SILC_AUTH_PUBLIC_KEY) {
	/* Get the public key */
	SilcPublicKey public_key;

	if (!silc_pkcs_load_public_key(config->clients->auth_data,
				       &public_key, SILC_PKCS_FILE_PEM))
	  if (!silc_pkcs_load_public_key(config->clients->auth_data,
					 &public_key, SILC_PKCS_FILE_BIN)) {
	    fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
		    config->filename, pc->linenum, 
		    (char *)config->clients->auth_data);
	    break;
	  }

	silc_free(config->clients->auth_data);
	config->clients->auth_data = (void *)public_key;
	config->clients->auth_data_len = 0;
      }

      /* Get port */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->clients->port = atoi(tmp);
	silc_free(tmp);
      }

      /* Get class number */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->clients->class = atoi(tmp);
	silc_free(tmp);
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->servers);

      /* Get host */
      ret = silc_config_get_token(line, &config->servers->host);
      if (ret < 0)
	break;
      if (ret == 0)
	/* Any host */
	config->servers->host = strdup("*");

      /* Get authentication method */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
	    strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
	  fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
	  config->servers->auth_meth = SILC_AUTH_PASSWORD;

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
	  config->servers->auth_meth = SILC_AUTH_PUBLIC_KEY;

	silc_free(tmp);
      }

      /* Get authentication data */
      ret = silc_config_get_token(line, (char **)&config->servers->auth_data);
      if (ret < 0)
	break;

      if (config->servers->auth_meth == SILC_AUTH_PASSWORD) {
	config->servers->auth_data_len = strlen(config->servers->auth_data);
      } else if (config->servers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
	/* Get the public key */
	SilcPublicKey public_key;

	if (!silc_pkcs_load_public_key(config->servers->auth_data,
				       &public_key, SILC_PKCS_FILE_PEM))
	  if (!silc_pkcs_load_public_key(config->servers->auth_data,
					 &public_key, SILC_PKCS_FILE_BIN)) {
	    fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
		    config->filename, pc->linenum, 
		    (char *)config->servers->auth_data);
	    break;
	  }

	silc_free(config->servers->auth_data);
	config->servers->auth_data = (void *)public_key;
	config->servers->auth_data_len = 0;
      }

      /* Get port */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->servers->port = atoi(tmp);
	silc_free(tmp);
      }

      /* Get version */
      ret = silc_config_get_token(line, &config->servers->version);
      if (ret < 0)
	break;

      /* Get class number */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->servers->class = atoi(tmp);
	silc_free(tmp);
      }

      /* Check whether this connection is backup router connection */
      ret = silc_config_get_token(line, &tmp);
      if (ret != -1) {
	config->servers->backup_router = atoi(tmp);
	if (config->servers->backup_router != 0)
	  config->servers->backup_router = TRUE;
	silc_free(tmp);
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->routers);

      /* Get host */
      ret = silc_config_get_token(line, &config->routers->host);
      if (ret < 0)
	break;

      /* Get authentication method */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
	    strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
	  fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
	  config->routers->auth_meth = SILC_AUTH_PASSWORD;

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
	  config->routers->auth_meth = SILC_AUTH_PUBLIC_KEY;

	silc_free(tmp);
      }

      /* Get authentication data */
      ret = silc_config_get_token(line, (char **)&config->routers->auth_data);
      if (ret < 0)
	break;

      if (config->routers->auth_meth == SILC_AUTH_PASSWORD) {
	config->routers->auth_data_len = strlen(config->routers->auth_data);
      } else if (config->routers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
	/* Get the public key */
	SilcPublicKey public_key;

	if (!silc_pkcs_load_public_key(config->routers->auth_data,
				       &public_key, SILC_PKCS_FILE_PEM))
	  if (!silc_pkcs_load_public_key(config->routers->auth_data,
					 &public_key, SILC_PKCS_FILE_BIN)) {
	    fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
		    config->filename, pc->linenum, 
		    (char *)config->routers->auth_data);
	    break;
	  }

	silc_free(config->routers->auth_data);
	config->routers->auth_data = (void *)public_key;
	config->routers->auth_data_len = 0;
      }

      /* Get port */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->routers->port = atoi(tmp);
	silc_free(tmp);
      }

      /* Get version */
      ret = silc_config_get_token(line, &config->routers->version);
      if (ret < 0)
	break;

      /* Get class number */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->routers->class = atoi(tmp);
	silc_free(tmp);
      }

      /* Get whether we are initiator or not */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	config->routers->initiator = atoi(tmp);
	if (config->routers->initiator != 0)
	  config->routers->initiator = TRUE;
	silc_free(tmp);
      }

      /* Get backup replace IP */
      ret = silc_config_get_token(line, &config->routers->backup_replace_ip);
      if (ret != -1)
	config->routers->backup_router = TRUE;

      if (config->routers->backup_router) {
	/* Get backup replace port */
	ret = silc_config_get_token(line, &tmp);
	if (ret != -1) {
	  config->routers->backup_replace_port = atoi(tmp);
	  silc_free(tmp);
	}
	
	/* Check whether the backup connection is local */
	ret = silc_config_get_token(line, &tmp);
	if (ret != -1) {
	  config->routers->backup_local = atoi(tmp);
	  if (config->routers->backup_local != 0)
	    config->routers->backup_local = TRUE;
	  silc_free(tmp);
	}
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->admins);

      /* Get host */
      ret = silc_config_get_token(line, &config->admins->host);
      if (ret < 0)
	break;
      if (ret == 0)
	/* Any host */
	config->admins->host = strdup("*");

      /* Get username */
      ret = silc_config_get_token(line, &config->admins->username);
      if (ret < 0)
	break;
      if (ret == 0)
	/* Any username */
	config->admins->username = strdup("*");

      /* Get nickname */
      ret = silc_config_get_token(line, &config->admins->nickname);
      if (ret < 0)
	break;
      if (ret == 0)
	/* Any nickname */
	config->admins->nickname = strdup("*");

      /* Get authentication method */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret) {
	if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
	    strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
	  fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
		  config->filename, pc->linenum, tmp);
	  break;
	}

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
	  config->admins->auth_meth = SILC_AUTH_PASSWORD;

	if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
	  config->admins->auth_meth = SILC_AUTH_PUBLIC_KEY;

	silc_free(tmp);
      }

      /* Get authentication data */
      ret = silc_config_get_token(line, (char **)&config->admins->auth_data);
      if (ret < 0)
	break;

      if (config->admins->auth_meth == SILC_AUTH_PASSWORD) {
	config->admins->auth_data_len = strlen(config->admins->auth_data);
      } else if (config->admins->auth_meth == SILC_AUTH_PUBLIC_KEY) {
	/* Get the public key */
	SilcPublicKey public_key;

	if (!silc_pkcs_load_public_key(config->admins->auth_data,
				       &public_key, SILC_PKCS_FILE_PEM))
	  if (!silc_pkcs_load_public_key(config->admins->auth_data,
					 &public_key, SILC_PKCS_FILE_BIN)) {
	    fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
		    config->filename, pc->linenum, 
		    (char *)config->admins->auth_data);
	    break;
	  }

	silc_free(config->admins->auth_data);
	config->admins->auth_data = (void *)public_key;
	config->admins->auth_data_len = 0;
      }

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:

      SILC_SERVER_CONFIG_LIST_ALLOC(config->denied);

      /* Get host */
      ret = silc_config_get_token(line, &config->denied->host);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Any host */
	config->denied->host = strdup("*");
	fprintf(stderr, "warning: %s:%d: Denying all connections",
		config->filename, pc->linenum);
      }

      /* Get port */
      ret = silc_config_get_token(line, &tmp);
      if (ret < 0)
	break;
      if (ret == 0) {
	/* Any port */
	config->denied->port = 0;
      } else {
	config->denied->port = atoi(tmp);
	silc_free(tmp);
      }

      /* Get comment */
      ret = silc_config_get_token(line, &config->denied->comment);
      if (ret < 0)
	break;

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_MOTD:

      if (!config->motd)
	config->motd = silc_calloc(1, sizeof(*config->motd));

      /* Get motd file */
      ret = silc_config_get_token(line, &config->motd->motd_file);
      if (ret < 0)
	break;

      check = TRUE;
      checkmask |= (1L << pc->section->type);
      break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_PID:

       if (!config->pidfile)
          config->pidfile = silc_calloc(1, sizeof(*config->pidfile));
          
       ret = silc_config_get_token(line, &config->pidfile->pid_file);
       if (ret < 0)
          break;
          
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;

    case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
    default:
      /* Error */
      break;
    }

    /* Check for error */
    if (check == FALSE) {
      /* Line could not be parsed */
      fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
      break;
    }

    pc = pc->next;
  }

  if (check == FALSE)
    return FALSE;;

  /* Check that all mandatory sections really were found. If not, the server
     cannot function and we return error. */
  ret = silc_server_config_check_sections(checkmask);
  if (ret == FALSE) {
    /* XXX */

  }
  
  /* Before returning all the lists in the config object must be set
     to their first values (the last value is first here). */
  while (config->cipher && config->cipher->prev)
    config->cipher = config->cipher->prev;
  while (config->pkcs && config->pkcs->prev)
    config->pkcs = config->pkcs->prev;
  while (config->hash_func && config->hash_func->prev)
    config->hash_func = config->hash_func->prev;
  while (config->hmac && config->hmac->prev)
    config->hmac = config->hmac->prev;
  while (config->listen_port && config->listen_port->prev)
    config->listen_port = config->listen_port->prev;
  while (config->logging && config->logging->prev)
    config->logging = config->logging->prev;
  while (config->conn_class && config->conn_class->prev)
    config->conn_class = config->conn_class->prev;
  while (config->clients && config->clients->prev)
    config->clients = config->clients->prev;
  while (config->servers && config->servers->prev)
    config->servers = config->servers->prev;
  while (config->admins && config->admins->prev)
    config->admins = config->admins->prev;
  while (config->routers && config->routers->prev)
    config->routers = config->routers->prev;
  
  SILC_LOG_DEBUG(("Done"));
  
  return TRUE;
}

/* This function checks that the mask sent as argument includes all the 
   sections that are mandatory in SILC server. */

int silc_server_config_check_sections(uint32 checkmask)
{
  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO))) {
    
    return FALSE;
  }
  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO))) {
    
    return FALSE;
  }
  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT))) {
    
    return FALSE;
  }
  if (!(checkmask & 
	(1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
    
    return FALSE;
  }
  if (!(checkmask 
	& (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION))) {
    
    return FALSE;
  }
  if (!(checkmask 
	& (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION))) {
    
    return FALSE;
  }

  return TRUE;
}

/* Sets log files where log messages is saved by the server. */

void silc_server_config_setlogfiles(SilcServerConfig config)
{
  SilcServerConfigSectionLogging *log;
  char *info, *warning, *error, *fatal;
  uint32 info_size, warning_size, error_size, fatal_size;

  SILC_LOG_DEBUG(("Setting configured log file names"));

  /* Set default files before checking configuration */
  info = SILC_LOG_FILE_INFO;
  warning = SILC_LOG_FILE_WARNING;
  error = SILC_LOG_FILE_ERROR;
  fatal = SILC_LOG_FILE_FATAL;
  info_size = 0;
  warning_size = 0;
  error_size = 0;
  fatal_size = 0;

  log = config->logging;
  while(log) {
    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_INFO)) {
      info = log->filename;
      info_size = log->maxsize;
    }
    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_WARNING)) {
      warning = log->filename;
      warning_size = log->maxsize;
    }
    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_ERROR)) {
      error = log->filename;
      error_size = log->maxsize;
    }
    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
      fatal = log->filename;
      fatal_size = log->maxsize;
    }

    log = log->next;
  }

  silc_log_set_files(info, info_size, warning, warning_size,
		     error, error_size, fatal, fatal_size);
}

/* Registers configured ciphers. These can then be allocated by the
   server when needed. */

bool silc_server_config_register_ciphers(SilcServerConfig config)
{
  SilcServerConfigSectionAlg *alg;
  SilcServer server = (SilcServer)config->server;

  SILC_LOG_DEBUG(("Registering configured ciphers"));

  if (!config->cipher)
    return FALSE;

  alg = config->cipher;
  while(alg) {

    if (!alg->sim_name) {
      int i;
      
      for (i = 0; silc_default_ciphers[i].name; i++)
	if (!strcmp(silc_default_ciphers[i].name, alg->alg_name)) {
	  silc_cipher_register(&silc_default_ciphers[i]);
	  break;
	}
      
      if (!silc_cipher_is_supported(alg->alg_name)) {
	SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
	silc_server_stop(server);
	exit(1);
      }
#ifdef SILC_SIM
    } else {
      /* Load (try at least) the crypto SIM module */
      SilcCipherObject cipher;
      SilcSimContext *sim;
      char *alg_name;

      memset(&cipher, 0, sizeof(cipher));
      cipher.name = alg->alg_name;
      cipher.block_len = alg->block_len;
      cipher.key_len = alg->key_len * 8;

      sim = silc_sim_alloc();
      sim->type = SILC_SIM_CIPHER;
      sim->libname = alg->sim_name;

      alg_name = strdup(alg->alg_name);
      if (strchr(alg_name, '-'))
	*strchr(alg_name, '-') = '\0';

      if ((silc_sim_load(sim))) {
	cipher.set_key = 
	  silc_sim_getsym(sim, silc_sim_symname(alg_name, 
						SILC_CIPHER_SIM_SET_KEY));
	SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
	cipher.set_key_with_string = 
	  silc_sim_getsym(sim, silc_sim_symname(alg_name, 
						SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
	SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
	cipher.encrypt = 
	  silc_sim_getsym(sim, silc_sim_symname(alg_name,
						SILC_CIPHER_SIM_ENCRYPT_CBC));
	SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
        cipher.decrypt = 
	  silc_sim_getsym(sim, silc_sim_symname(alg_name,
						SILC_CIPHER_SIM_DECRYPT_CBC));
	SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
        cipher.context_len = 
	  silc_sim_getsym(sim, silc_sim_symname(alg_name,
						SILC_CIPHER_SIM_CONTEXT_LEN));
	SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));

	/* Put the SIM to the list of all SIM's in server */
	silc_dlist_add(server->sim, sim);

	silc_free(alg_name);
      } else {
	SILC_LOG_ERROR(("Error configuring ciphers"));
	silc_server_stop(server);
	exit(1);
      }

      /* Register the cipher */
      silc_cipher_register(&cipher);
#endif
    }

    alg = alg->next;
  }

  return TRUE;
}

/* Registers configured PKCS's. */

bool silc_server_config_register_pkcs(SilcServerConfig config)
{
  SilcServerConfigSectionAlg *alg = config->pkcs;
  SilcServer server = (SilcServer)config->server;

  SILC_LOG_DEBUG(("Registering configured PKCS"));

  if (!config->pkcs)
    return FALSE;

  while(alg) {
    int i;
    
    for (i = 0; silc_default_pkcs[i].name; i++)
      if (!strcmp(silc_default_pkcs[i].name, alg->alg_name)) {
	silc_pkcs_register(&silc_default_pkcs[i]);
	break;
      }
      
    if (!silc_pkcs_is_supported(alg->alg_name)) {
      SILC_LOG_ERROR(("Unknown PKCS `%s'", alg->alg_name));
      silc_server_stop(server);
      exit(1);
    }

    alg = alg->next;
  }

  return TRUE;
}

/* Registers configured hash functions. These can then be allocated by the
   server when needed. */

bool silc_server_config_register_hashfuncs(SilcServerConfig config)
{
  SilcServerConfigSectionAlg *alg;
  SilcServer server = (SilcServer)config->server;

  SILC_LOG_DEBUG(("Registering configured hash functions"));

  if (!config->hash_func)
    return FALSE;

  alg = config->hash_func;
  while(alg) {

    if (!alg->sim_name) {
      int i;
      
      for (i = 0; silc_default_hash[i].name; i++)
	if (!strcmp(silc_default_hash[i].name, alg->alg_name)) {
	  silc_hash_register(&silc_default_hash[i]);
	  break;
	}
      
      if (!silc_hash_is_supported(alg->alg_name)) {
	SILC_LOG_ERROR(("Unknown hash funtion `%s'", alg->alg_name));
	silc_server_stop(server);
	exit(1);
      }

#ifdef SILC_SIM
    } else {
      /* Load (try at least) the hash SIM module */
      SilcHashObject hash;
      SilcSimContext *sim;

      memset(&hash, 0, sizeof(hash));
      hash.name = alg->alg_name;
      hash.block_len = alg->block_len;
      hash.hash_len = alg->key_len;

      sim = silc_sim_alloc();
      sim->type = SILC_SIM_HASH;
      sim->libname = alg->sim_name;

      if ((silc_sim_load(sim))) {
	hash.init = 
	  silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
						SILC_HASH_SIM_INIT));
	SILC_LOG_DEBUG(("init=%p", hash.init));
	hash.update = 
	  silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
						SILC_HASH_SIM_UPDATE));
	SILC_LOG_DEBUG(("update=%p", hash.update));
        hash.final = 
	  silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
						SILC_HASH_SIM_FINAL));
	SILC_LOG_DEBUG(("final=%p", hash.final));
        hash.context_len = 
	  silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
						SILC_HASH_SIM_CONTEXT_LEN));
	SILC_LOG_DEBUG(("context_len=%p", hash.context_len));

	/* Put the SIM to the table of all SIM's in server */
	silc_dlist_add(server->sim, sim);
      } else {
	SILC_LOG_ERROR(("Error configuring hash functions"));
	silc_server_stop(server);
	exit(1);
      }

      /* Register the hash function */
      silc_hash_register(&hash);
#endif
    }

    alg = alg->next;
  }

  return TRUE;
}

/* Registers configure HMACs. These can then be allocated by the server
   when needed. */

bool silc_server_config_register_hmacs(SilcServerConfig config)
{
  SilcServerConfigSectionAlg *alg;
  SilcServer server = (SilcServer)config->server;

  SILC_LOG_DEBUG(("Registering configured HMACs"));

  if (!config->hmac)
    return FALSE;

  alg = config->hmac;
  while(alg) {
    SilcHmacObject hmac;
    
    if (!silc_hash_is_supported(alg->sim_name)) {
      SILC_LOG_ERROR(("Unknown hash function `%s'", alg->sim_name));
      silc_server_stop(server);
      exit(1);
    }
    
    /* Register the HMAC */
    memset(&hmac, 0, sizeof(hmac));
    hmac.name = alg->alg_name;
    hmac.len = alg->key_len;
    silc_hmac_register(&hmac);

    alg = alg->next;
  }

  return TRUE;
}

/* Returns client authentication information from server configuration
   by host (name or ip). If `port' is non-null then both name or IP and 
   the port must match. */

SilcServerConfigSectionClientConnection *
silc_server_config_find_client_conn(SilcServerConfig config, 
				    char *host, int port)
{
  int i;
  SilcServerConfigSectionClientConnection *client = NULL;
  bool match = FALSE;

  if (!host)
    return NULL;

  if (!config->clients)
    return NULL;

  client = config->clients;

  for (i = 0; client; i++) {
    if (silc_string_compare(client->host, host))
      match = TRUE;

    if (port && client->port && client->port != port)
      match = FALSE;

    if (match)
      break;

    client = client->next;
  }

  if (!client)
    return NULL;

  return client;
}

/* Returns server connection info from server configuartion by host 
   (name or ip). If `port' is non-null then both name or IP and the port
   must match. */

SilcServerConfigSectionServerConnection *
silc_server_config_find_server_conn(SilcServerConfig config, 
				    char *host, int port)
{
  int i;
  SilcServerConfigSectionServerConnection *serv = NULL;
  bool match = FALSE;

  if (!host)
    return NULL;

  if (!config->servers)
    return NULL;

  serv = config->servers;
  for (i = 0; serv; i++) {
    if (silc_string_compare(serv->host, host))
      match = TRUE;

    if (port && serv->port && serv->port != port)
      match = FALSE;

    if (match)
      break;

    serv = serv->next;
  }

  if (!serv)
    return NULL;

  return serv;
}

/* Returns router connection info from server configuartion by
   host (name or ip). */

SilcServerConfigSectionServerConnection *
silc_server_config_find_router_conn(SilcServerConfig config, 
				    char *host, int port)
{
  int i;
  SilcServerConfigSectionServerConnection *serv = NULL;
  bool match = FALSE;

  if (!host)
    return NULL;

  if (!config->routers)
    return NULL;

  serv = config->routers;
  for (i = 0; serv; i++) {
    if (silc_string_compare(serv->host, host))
      match = TRUE;

    if (port && serv->port && serv->port != port)
      match = FALSE;

    if (match)
      break;

    serv = serv->next;
  }

  if (!serv)
    return NULL;

  return serv;
}

/* Returns TRUE if configuartion for a router connection that we are 
   initiating exists. */

bool silc_server_config_is_primary_route(SilcServerConfig config)
{
  int i;
  SilcServerConfigSectionServerConnection *serv = NULL;
  bool found = FALSE;

  serv = config->routers;
  for (i = 0; serv; i++) {
    if (serv->initiator == TRUE && serv->backup_router == FALSE) {
      found = TRUE;
      break;
    }

    serv = serv->next;
  }

  return found;
}

/* Returns our primary connection configuration or NULL if we do not
   have primary router configured. */

SilcServerConfigSectionServerConnection *
silc_server_config_get_primary_router(SilcServerConfig config)
{
  int i;
  SilcServerConfigSectionServerConnection *serv = NULL;

  serv = config->routers;
  for (i = 0; serv; i++) {
    if (serv->initiator == TRUE && serv->backup_router == FALSE)
      return serv;
    serv = serv->next;
  }

  return NULL;
}

/* Returns Admin connection configuration by host, username and/or 
   nickname. */

SilcServerConfigSectionAdminConnection *
silc_server_config_find_admin(SilcServerConfig config,
			      char *host, char *username, char *nickname)
{
  SilcServerConfigSectionAdminConnection *admin = NULL;
  int i;

  if (!config->admins)
    return NULL;

  if (!host)
    host = "*";
  if (!username)
    username = "*";
  if (!nickname)
    nickname = "*";

  admin = config->admins;
  for (i = 0; admin; i++) {
    if (silc_string_compare(admin->host, host) &&
	silc_string_compare(admin->username, username) &&
	silc_string_compare(admin->nickname, nickname))
      break;

    admin = admin->next;
  }

  if (!admin)
    return NULL;

  return admin;
}

/* Returns the Denied connection configuration by host and port. */

SilcServerConfigSectionDenyConnection *
silc_server_config_denied_conn(SilcServerConfig config, char *host,
			       int port)
{
  int i;
  SilcServerConfigSectionDenyConnection *deny = NULL;
  bool match = FALSE;

  if (!host)
    return NULL;

  if (!config->denied)
    return NULL;

  deny = config->denied;
  for (i = 0; deny; i++) {
    if (silc_string_compare(deny->host, host))
      match = TRUE;

    if (port && deny->port && deny->port != port)
      match = FALSE;

    if (match)
      break;

    deny = deny->next;
  }

  if (!deny)
    return NULL;

  return deny;
}
