/*

  server_util.c

  Author: Pekka Riikonen <priikone@silcnet.org>

  Copyright (C) 1997 - 2006 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; version 2 of the License.

  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$ */

#include "silc.h"
#include "silcserver.h"
#include "server_internal.h"

/* Return next available command identifier. */

SilcUInt16 silc_server_cmd_ident(SilcServer server)
{
  SilcUInt16 cmd_ident;

  silc_mutex_lock(server->lock);
  cmd_ident = ++server->cmd_ident;
  silc_mutex_unlock(server->lock);

  return cmd_ident;
}

/* XXX locking */

SilcBool silc_server_check_watcher_list(SilcServer server,
				    SilcClientEntry client,
				    const char *new_nick,
				    SilcNotifyType notify)
{
  return TRUE;
}

/* This function is used to send the notify packets and motd to the
   incoming client connection. */

void silc_server_send_welcome(SilcServerAccept ac, SilcClientEntry client)
{
  SilcServer server = ac->thread->server;
  SilcPacketStream stream = client->stream;

  SILC_LOG_DEBUG(("Send welcome notifys"));

  /* Send some nice info to the client */
  SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			  ("Welcome to the SILC Network %s",
			   client->username));
  SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			  ("Your host is %s, running version %s",
			   server->server_name, SILC_DIST_VERSION_STRING));

  if (server->server_type == SILC_ROUTER) {
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("There are %d clients, %d servers and %d "
			     "routers in SILC Network",
			     server->stat.clients, server->stat.servers,
			     server->stat.routers));
  } else {
    if (server->stat.clients && server->stat.servers + 1)
      SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			      ("There are %d clients, %d servers and %d "
			       "routers in SILC Network",
			       server->stat.clients, server->stat.servers,
			       (server->standalone ? 0 :
				!server->stat.routers ? 1 :
				server->stat.routers)));
  }

  if (server->stat.cell_clients && server->stat.cell_servers + 1)
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("There are %d clients on %d servers in our cell",
			     server->stat.cell_clients,
			     server->stat.cell_servers));
  if (server->server_type == SILC_ROUTER) {
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("I have %d clients, %d channels, %d servers and "
			     "%d routers",
			     server->stat.my_clients,
			     server->stat.my_channels,
			     server->stat.my_servers,
			     server->stat.my_routers));
  } else {
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("I have %d clients and %d channels formed",
			     server->stat.my_clients,
			     server->stat.my_channels));
  }

  if (server->stat.server_ops || server->stat.router_ops)
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("There are %d server operators and %d router "
			     "operators online",
			     server->stat.server_ops,
			     server->stat.router_ops));
  if (server->stat.my_router_ops + server->stat.my_server_ops)
    SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			    ("I have %d operators online",
			     server->stat.my_router_ops +
			     server->stat.my_server_ops));

  SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			  ("Your connection is secured with %s cipher, "
			   "key length %d bits",
			   silc_cipher_get_name(ac->prop->cipher),
			   silc_cipher_get_key_len(ac->prop->cipher)));
  SILC_SERVER_SEND_NOTIFY(server, stream, SILC_NOTIFY_TYPE_NONE,
			  ("Your current nickname is %s",
			   client->nickname));

  /* Send motd */
  silc_server_send_motd(server, stream);
}

/* Creates new Client ID. */

SilcBool silc_server_create_client_id(SilcServer server, char *nickname,
				      SilcClientID *new_id)
{
  unsigned char hash[16];
  SilcBool finding = FALSE;

  SILC_LOG_DEBUG(("Creating new Client ID"));

  /* Create hash of the nickname (it's already checked as valid identifier
     string). */
  silc_hash_make(server->md5hash, nickname, strlen(nickname), hash);

  /* Create the ID */
  memset(new_id, 0, sizeof(*new_id));
  memcpy(new_id->ip.data, server->id.ip.data, server->id.ip.data_len);
  new_id->ip.data_len = server->id.ip.data_len;
  new_id->rnd = silc_rng_get_byte(server->rng);
  memcpy(new_id->hash, hash, CLIENTID_HASH_LEN);

  /* Assure that the ID does not exist already */
  while (1) {
    if (!silc_server_find_client_by_id(server, new_id, FALSE, NULL))
      break;

    /* The ID exists, start increasing the rnd from 0 until we find a
       ID that does not exist. If we wrap and it still exists then we
       will return FALSE and the caller must send some other nickname
       since this cannot be used anymore. */
    new_id->rnd++;

    if (finding && new_id->rnd == 0)
      return FALSE;

    if (!finding) {
      new_id->rnd = 0;
      finding = TRUE;
    }
  }

  SILC_LOG_DEBUG(("New ID (%s)", silc_id_render(new_id, SILC_ID_CLIENT)));

  return TRUE;
}

/* Creates a Server ID. */

SilcBool silc_server_create_server_id(SilcServer server,
				      const char *ip, SilcUInt16 port,
				      SilcServerID *new_id)
{
  SILC_LOG_DEBUG(("Creating new Server ID"));

  if (!new_id)
    return FALSE;

  /* Create the ID */

  if (!silc_net_addr2bin(ip, new_id->ip.data, sizeof(new_id->ip.data)))
    return FALSE;

  new_id->ip.data_len = silc_net_is_ip4(ip) ? 4 : 16;
  new_id->port = SILC_SWAB_16(port);
  new_id->rnd = silc_rng_get_rn16(server->rng);

  SILC_LOG_DEBUG(("New ID (%s)", silc_id_render(new_id, SILC_ID_SERVER)));

  return TRUE;
}

/* Checks whether the `server_id' is valid.  It must be based to the
   IP address provided in the `remote' socket connection. */

SilcBool silc_server_check_server_id(const char *ip_address,
				     SilcServerID *server_id)
{
  unsigned char ip[16];

  if (!silc_net_addr2bin(ip_address, ip, sizeof(ip)))
    return FALSE;

  if (silc_net_is_ip4(ip_address)) {
    if (!memcmp(server_id->ip.data, ip, 4))
      return TRUE;
  } else {
    if (!memcmp(server_id->ip.data, ip, 16))
      return TRUE;
  }

  return FALSE;
}
