/*

  server_st_command.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.

*/

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

/************************** Types and definitions ***************************/

#define SILC_SERVER_COMMAND_CHECK(min, max)				\
do {									\
  SilcUInt32 _argc;							\
									\
  SILC_LOG_DEBUG(("Start"));						\
									\
  _argc = silc_argument_get_arg_num(args);				\
  if (_argc < min) {							\
    SILC_LOG_DEBUG(("Not enough parameters in command"));		\
    silc_server_command_status_reply(cmd,				\
				     silc_command_get(cmd->payload),	\
				     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,	\
				     0);				\
    silc_server_command_free(cmd);					\
    return SILC_FSM_FINISH;						\
  }									\
  if (_argc > max) {							\
    SILC_LOG_DEBUG(("Too many parameters in command"));			\
    silc_server_command_status_reply(cmd,				\
				     silc_command_get(cmd->payload),	\
				     SILC_STATUS_ERR_TOO_MANY_PARAMS,	\
				     0);				\
    silc_server_command_free(cmd);					\
    return SILC_FSM_FINISH;						\
  }									\
} while(0)


/************************ Static utility functions **************************/

/* Sends simple status message as command reply packet */

static void
silc_server_command_status_reply(SilcServerCommand cmd,
				      SilcCommand command,
				      SilcStatus status,
				      SilcStatus error)
{
  SilcBuffer buffer;

  /* Statistics */
  cmd->thread->server->stat.commands_sent++;

  SILC_LOG_DEBUG(("Sending command status %d", status));
  buffer =
    silc_command_reply_payload_encode_va(command, status, error,
					 silc_command_get_ident(cmd->payload),
					 0);
  silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
		   buffer->data, silc_buffer_len(buffer));
  silc_buffer_free(buffer);
}

/* Sends command status reply with one extra argument. The argument
   type must be sent as argument. */

static void
silc_server_command_status_data(SilcServerCommand cmd,
				     SilcCommand command,
				     SilcStatus status,
				     SilcStatus error,
				     SilcUInt32 arg_type,
				     const unsigned char *arg,
				     SilcUInt32 arg_len)
{
  SilcBuffer buffer;

  /* Statistics */
  cmd->thread->server->stat.commands_sent++;

  SILC_LOG_DEBUG(("Sending command status %d", status));

  buffer =
    silc_command_reply_payload_encode_va(command, status, 0,
					 silc_command_get_ident(cmd->payload),
					 1, arg_type, arg, arg_len);
  silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
		   buffer->data, silc_buffer_len(buffer));
  silc_buffer_free(buffer);
}

static void
silc_server_command_status_data2(SilcServerCommand cmd,
				      SilcCommand command,
				      SilcStatus status,
				      SilcStatus error,
				      SilcUInt32 arg_type1,
				      const unsigned char *arg1,
				      SilcUInt32 arg_len1,
				      SilcUInt32 arg_type2,
				      const unsigned char *arg2,
				      SilcUInt32 arg_len2)
{
  SilcBuffer buffer;

  /* Statistics */
  cmd->thread->server->stat.commands_sent++;

  SILC_LOG_DEBUG(("Sending command status %d", status));

  buffer =
    silc_command_reply_payload_encode_va(command, status, 0,
					 silc_command_get_ident(cmd->payload),
					 2, arg_type1, arg1, arg_len1,
					 arg_type2, arg2, arg_len2);
  silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
		   buffer->data, silc_buffer_len(buffer));
  silc_buffer_free(buffer);
}

void silc_server_command_pending_free(SilcServerThread thread,
				      SilcServerPending pending);


/**************************** Utility functions *****************************/

/* Gets command context from freelist or allocates a new one. */

SilcServerCommand silc_server_command_alloc(SilcServerThread thread)
{
  SilcServerCommand cmd;

  silc_mutex_lock(thread->server->lock);

  /* Get command context from freelist or allocate new one. */
  cmd = silc_list_get(thread->server->command_pool);
  if (!cmd) {
    silc_mutex_unlock(thread->server->lock);

    cmd = silc_calloc(1, sizeof(*cmd));
    if (!cmd)
      return NULL;

    SILC_LOG_DEBUG(("Allocating command context %p", cmd));

    cmd->thread = thread;

    return cmd;
  }

  SILC_LOG_DEBUG(("Get command context %p", cmd));

  /* Delete from freelist */
  silc_list_del(thread->server->command_pool, cmd);

  cmd->thread = thread;

  silc_mutex_unlock(thread->server->lock);

  return cmd;
}

/* Puts the command context back to freelist */

void silc_server_command_free(SilcServerCommand cmd)
{
  SilcServerThread thread = cmd->thread;

  silc_mutex_lock(thread->server->lock);

#if defined(SILC_DEBUG)
  /* Check for double free */
  assert(cmd->packet != NULL);
#endif /* SILC_DEBUG */

  if (cmd->packet)
    silc_packet_free(cmd->packet);
  cmd->packet = NULL;

  if (cmd->pending)
    silc_server_command_pending_free(thread, cmd->pending);

  /* Put the packet back to freelist */
  silc_list_add(thread->server->command_pool, cmd);

  silc_mutex_unlock(thread->server->lock);
}

/* Returns pending context used to wait for a command reply. */

SilcServerPending silc_server_command_pending(SilcServerThread thread,
					      SilcUInt16 cmd_ident)
{
  SilcServerPending pending;

  silc_mutex_lock(thread->server->lock);

  /* Check if pending already */
  if (silc_hash_table_find(thread->server->pending_commands,
			   SILC_32_TO_PTR(cmd_ident), NULL,
			   (void **)&pending)) {
    pending->refcnt++;
    silc_mutex_unlock(thread->server->lock);
    return pending;
  }

  pending = silc_calloc(1, sizeof(*pending));
  if (!pending) {
    silc_mutex_unlock(thread->server->lock);
    return NULL;
  }

  silc_fsm_event_init(&pending->wait_reply, &thread->fsm, 0);
  pending->refcnt = 1;
  pending->cmd_ident = cmd_ident;

  /* Add to pending commands hash table */
  if (!silc_hash_table_add(thread->server->pending_commands,
			   SILC_32_TO_PTR(cmd_ident), pending)) {
    silc_mutex_unlock(thread->server->lock);
    silc_free(pending);
    return NULL;
  }

  silc_mutex_unlock(thread->server->lock);

  return pending;
}

/* Free's the pending command context */

void silc_server_command_pending_free(SilcServerThread thread,
				      SilcServerPending pending)
{
  silc_mutex_lock(thread->server->lock);

  pending->refcnt--;
  if (pending->refcnt > 0) {
    silc_mutex_unlock(thread->server->lock);
    return;
  }

  /* If command reply context set, free it also */
  if (pending->reply) {
    pending->reply->pending = NULL;
    silc_server_command_free(pending->reply);
  }

  /* Remove from pending commands */
  silc_hash_table_del_by_context(thread->server->pending_commands,
				 SILC_32_TO_PTR(pending->cmd_ident), pending);
  silc_free(pending);

  silc_mutex_unlock(thread->server->lock);
}

/* Returns pending command context for command identifier */

SilcServerPending silc_server_command_pending_get(SilcServerThread thread,
						  SilcUInt16 cmd_ident)
{
  SilcServerPending pending = NULL;

  silc_mutex_lock(thread->server->lock);
  silc_hash_table_find(thread->server->pending_commands,
		       SILC_32_TO_PTR(cmd_ident), NULL, (void **)&pending);
  silc_mutex_unlock(thread->server->lock);

  return pending;
}

/* Signals pending command waiters.  Used by command reply routines. */

void silc_server_command_pending_signal(SilcServerCommand cmd)
{
  SilcServerThread thread = cmd->thread;
  SilcServerPending pending = cmd->pending;

  if (!pending)
    return;

  silc_mutex_lock(thread->server->lock);

  /* Signal */
  pending->reply = cmd;
  SILC_FSM_EVENT_SIGNAL(&pending->wait_reply);

  /* Remove from pending */
  silc_hash_table_del_by_context(thread->server->pending_commands,
				 SILC_32_TO_PTR(pending->cmd_ident), pending);

  silc_mutex_unlock(thread->server->lock);
}


/**************************** Command received ******************************/

/* Received a COMMAND packet.  We parse the packet and process the
   requested command. */

SILC_FSM_STATE(silc_server_st_packet_command)
{
  SilcServerThread thread = fsm_context;
  SilcPacket packet = state_context;
  SilcEntryData data = silc_packet_get_context(packet->stream);
  SilcServerCommand cmd;
  SilcUInt32 timeout = 0;

  /* Allocate command context. */
  cmd = silc_server_command_alloc(thread);
  if (!cmd) {
    silc_packet_free(packet);
    return SILC_FSM_FINISH;
  }

  cmd->packet = packet;

  /* Parse the command payload in the packet */
  cmd->payload = silc_command_payload_parse(packet->buffer.data,
					    silc_buffer_len(&packet->buffer));
  if (!cmd->payload) {
    SILC_LOG_ERROR(("Bad command payload"));
    silc_server_command_free(cmd);
    return SILC_FSM_FINISH;
  }

  /* If client executes commands more frequently than once in 2 seconds,
     apply 0 - 2 seconds of timeout to prevent flooding. */
  if (data->type == SILC_CONN_CLIENT) {
    SilcClientEntry client = (SilcClientEntry)data;

    if (client->last_command && (time(NULL) - client->last_command) < 2) {
      client->fast_command++;
      if (client->fast_command > 5)
	timeout = (client->fast_command < 3 ? 0 :
		   2 - (time(NULL) - client->last_command));
    } else {
      if (client->fast_command - 2 <= 0)
	client->fast_command = 0;
      else
	client->fast_command -= 2;
    }

    client->last_command = time(NULL) + timeout;
  }

  silc_fsm_set_state_context(fsm, cmd);

  SILC_LOG_DEBUG(("Processing %s command (%d timeout)",
		  silc_get_command_name(silc_command_get(cmd->payload)),
		  timeout));

  /* Process command */
  switch (silc_command_get(cmd->payload)) {

  case SILC_COMMAND_WHOIS:
    /** Command WHOIS */
    silc_fsm_next_later(fsm, silc_server_st_command_whois, timeout, 0);
    break;

  case SILC_COMMAND_WHOWAS:
    /** Command WHOWAS */
    silc_fsm_next_later(fsm, silc_server_st_command_whowas, timeout, 0);
    break;

  case SILC_COMMAND_IDENTIFY:
    /** Command IDENTIFY */
    silc_fsm_next_later(fsm, silc_server_st_command_identify, timeout, 0);
    break;

  case SILC_COMMAND_NICK:
    /** Command NICK */
    silc_fsm_next_later(fsm, silc_server_st_command_nick, timeout, 0);
    break;

  case SILC_COMMAND_LIST:
    /** Command LIST */
    silc_fsm_next_later(fsm, silc_server_st_command_list, timeout, 0);
    break;

  case SILC_COMMAND_TOPIC:
    /** Command TOPIC */
    silc_fsm_next_later(fsm, silc_server_st_command_topic, timeout, 0);
    break;

  case SILC_COMMAND_INVITE:
    /** Command INVITE */
    silc_fsm_next_later(fsm, silc_server_st_command_invite, timeout, 0);
    break;

  case SILC_COMMAND_QUIT:
    /** Command QUIT */
    silc_fsm_next_later(fsm, silc_server_st_command_quit, timeout, 0);
    break;

  case SILC_COMMAND_KILL:
    /** Command KILL */
    silc_fsm_next_later(fsm, silc_server_st_command_kill, timeout, 0);
    break;

  case SILC_COMMAND_INFO:
    /** Command INFO */
    silc_fsm_next_later(fsm, silc_server_st_command_info, timeout, 0);
    break;

  case SILC_COMMAND_STATS:
    /** Command STATS */
    silc_fsm_next_later(fsm, silc_server_st_command_stats, timeout, 0);
    break;

  case SILC_COMMAND_PING:
    /** Command INFO */
    silc_fsm_next_later(fsm, silc_server_st_command_ping, timeout, 0);
    break;

  case SILC_COMMAND_OPER:
    /** Command OPER */
    silc_fsm_next_later(fsm, silc_server_st_command_oper, timeout, 0);
    break;

  case SILC_COMMAND_JOIN:
    /** Command JOIN */
    silc_fsm_next_later(fsm, silc_server_st_command_join, timeout, 0);
    break;

  case SILC_COMMAND_MOTD:
    /** Command MOTD */
    silc_fsm_next_later(fsm, silc_server_st_command_motd, timeout, 0);
    break;

  case SILC_COMMAND_UMODE:
    /** Command UMODE */
    silc_fsm_next_later(fsm, silc_server_st_command_umode, timeout, 0);
    break;

  case SILC_COMMAND_CMODE:
    /** Command CMODE */
    silc_fsm_next_later(fsm, silc_server_st_command_cmode, timeout, 0);
    break;

  case SILC_COMMAND_CUMODE:
    /** Command CUMODE */
    silc_fsm_next_later(fsm, silc_server_st_command_cumode, timeout, 0);
    break;

  case SILC_COMMAND_KICK:
    /** Command KICK */
    silc_fsm_next_later(fsm, silc_server_st_command_kick, timeout, 0);
    break;

  case SILC_COMMAND_BAN:
    /** Command BAN */
    silc_fsm_next_later(fsm, silc_server_st_command_ban, timeout, 0);
    break;

  case SILC_COMMAND_DETACH:
    /** Command DETACH */
    silc_fsm_next_later(fsm, silc_server_st_command_detach, timeout, 0);
    break;

  case SILC_COMMAND_WATCH:
    /** Command WATCH */
    silc_fsm_next_later(fsm, silc_server_st_command_watch, timeout, 0);
    break;

  case SILC_COMMAND_SILCOPER:
    /** Command SILCOPER */
    silc_fsm_next_later(fsm, silc_server_st_command_silcoper, timeout, 0);
    break;

  case SILC_COMMAND_LEAVE:
    /** Command LEAVE */
    silc_fsm_next_later(fsm, silc_server_st_command_leave, timeout, 0);
    break;

  case SILC_COMMAND_USERS:
    /** Command USERS */
    silc_fsm_next_later(fsm, silc_server_st_command_users, timeout, 0);
    break;

  case SILC_COMMAND_GETKEY:
    /** Command GETKEY */
    silc_fsm_next_later(fsm, silc_server_st_command_getkey, timeout, 0);
    break;

  case SILC_COMMAND_SERVICE:
    /** Command SERVICE */
    silc_fsm_next_later(fsm, silc_server_st_command_service, timeout, 0);
    break;

  default:
    SILC_LOG_DEBUG(("Unknown command %d", silc_command_get(cmd->payload)));
    silc_server_command_free(cmd);
    return SILC_FSM_FINISH;
    break;
  }

  /* Statistics */
  thread->server->stat.commands_received++;

  return timeout ? SILC_FSM_WAIT : return SILC_FSM_CONTINUE;
}

/********************************* WHOIS ************************************/

SILC_FSM_STATE(silc_server_st_command_whois)
{
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  SILC_SERVER_COMMAND_CHECK(1, 256);

  /** WHOIS query */
  silc_fsm_next(fsm, silc_server_st_query_whois);

  return SILC_FSM_CONTINUE;
}


/********************************* WHOWAS ***********************************/

SILC_FSM_STATE(silc_server_st_command_whowas)
{
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  SILC_SERVER_COMMAND_CHECK(1, 2);

  /** WHOWAS query */
  silc_fsm_next(fsm, silc_server_st_query_whowas);

  return SILC_FSM_CONTINUE;
}


/******************************** IDENTIFY **********************************/

SILC_FSM_STATE(silc_server_st_command_identify)
{
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  SILC_SERVER_COMMAND_CHECK(1, 256);

  /** IDENTIFY query */
  silc_fsm_next(fsm, silc_server_st_query_identify);

  return SILC_FSM_CONTINUE;
}


/********************************** NICK ************************************/

SILC_FSM_STATE(silc_server_st_command_nick)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
  SilcClientEntry client = silc_packet_get_context(cmd->packet->stream);
  SilcBuffer nidp, oidp = NULL;
  SilcClientID new_id;
  SilcUInt32 nick_len;
  unsigned char *nick, *nickc;
  SilcUInt16 ident = silc_command_get_ident(cmd->payload);

  SILC_SERVER_COMMAND_CHECK(1, 1);

  /* This command can come only from client */
  if (!SILC_IS_CLIENT(client)) {
    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
				     SILC_STATUS_ERR_OPERATION_ALLOWED, 0);
    goto out;
  }

  /* Get nickname */
  nick = silc_argument_get_arg_type(args, 1, &nick_len);
  if (!nick) {
    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
				     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
    goto out;
  }

  /* Truncate over long nicks */
  if (nick_len > 128) {
    nick_len = 128;
    nick[nick_len - 1] = '\0';
  }

  /* Check for same nickname */
  if (strlen(client->nickname) == nick_len &&
      !memcmp(client->nickname, nick, nick_len)) {
    nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);
    goto send_reply;
  }

  /* Check for valid nickname string. */
  nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL);
  if (!nickc) {
    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
				     SILC_STATUS_ERR_BAD_NICKNAME, 0);
    goto out;
  }

  /* Create new Client ID */
  if (!silc_server_create_client_id(thread->server, nickc, &new_id)) {
    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
				     SILC_STATUS_ERR_OPERATION_ALLOWED, 0);
    goto out;
  }
  silc_free(nickc);

  oidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);

  /* Replace the old nickname and ID with new ones.  This checks for
     validity of the nickname too. */
  if (!silc_server_replace_client_id(thread->server, &client->id, &new_id,
				     nick)) {
    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
				     SILC_STATUS_ERR_BAD_NICKNAME, 0);
    goto out;
  }

  nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);

#if 0
  /* Send notify about nickname and ID change to network. */
  silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
				      SILC_BROADCAST(server), client->id,
				      &new_id, nick);

  /* Send NICK_CHANGE notify to the client's channels */
  silc_server_send_notify_on_channels(server, NULL, client,
				      SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
				      oidp->data, silc_buffer_len(oidp),
				      nidp->data, silc_buffer_len(nidp),
				      client->nickname,
				      strlen(client->nickname));
#endif

 send_reply:
  /* Send the new Client ID as reply command back to client */
  silc_server_send_command_reply(thread->server, cmd->packet->stream,
				 SILC_COMMAND_NICK,
				 SILC_STATUS_OK, 0, ident, 2,
				 2, nidp->data, silc_buffer_len(nidp),
				 3, nick, nick_len);
  silc_buffer_free(nidp);
  silc_buffer_free(oidp);

 out:
  silc_server_command_free(cmd);
  return SILC_FSM_FINISH;
}


/********************************** LIST ************************************/

SILC_FSM_STATE(silc_server_st_command_list)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** TOPIC ***********************************/

SILC_FSM_STATE(silc_server_st_command_topic)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************* INVITE ***********************************/

SILC_FSM_STATE(silc_server_st_command_invite)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** QUIT ************************************/

SILC_FSM_STATE(silc_server_st_command_quit)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** KILL ************************************/

SILC_FSM_STATE(silc_server_st_command_kill)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** INFO ************************************/

SILC_FSM_STATE(silc_server_st_command_info)
{
  return SILC_FSM_FINISH;
}


/********************************** STATS ***********************************/

SILC_FSM_STATE(silc_server_st_command_stats)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** PING ************************************/

SILC_FSM_STATE(silc_server_st_command_ping)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
  SilcUInt32 tmp_len;
  unsigned char *tmp;
  SilcID id;

  SILC_SERVER_COMMAND_CHECK(1, 1);

  /* Get Server ID */
  tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
  if (!tmp) {
    silc_server_command_status_reply(cmd, silc_command_get(cmd->payload),
				     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
    goto out;
  }
  if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) {
    silc_server_command_status_data(cmd, silc_command_get(cmd->payload),
				    SILC_STATUS_ERR_BAD_SERVER_ID, 0,
				    2, tmp, tmp_len);
    goto out;
  }

  /* Must be our ID */
  if (!SILC_ID_SERVER_COMPARE(&id.u.server_id, &thread->server->id)) {
    silc_server_command_status_data(cmd, silc_command_get(cmd->payload),
				    SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
				    2, tmp, tmp_len);
    goto out;
  }

  /* Send our reply */
  silc_server_command_status_reply(cmd, silc_command_get(cmd->payload),
				   SILC_STATUS_OK, 0);

 out:
  silc_server_command_free(cmd);
  return SILC_FSM_FINISH;
}


/*********************************** OPER ***********************************/

SILC_FSM_STATE(silc_server_st_command_oper)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** JOIN ***********************************/

SILC_FSM_STATE(silc_server_st_command_join)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** MOTD ***********************************/

SILC_FSM_STATE(silc_server_st_command_motd)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** UMODE **********************************/

SILC_FSM_STATE(silc_server_st_command_umode)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** CMODE **********************************/

SILC_FSM_STATE(silc_server_st_command_cmode)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** CUMODE **********************************/

SILC_FSM_STATE(silc_server_st_command_cumode)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** KICK ***********************************/

SILC_FSM_STATE(silc_server_st_command_kick)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/*********************************** BAN ************************************/

SILC_FSM_STATE(silc_server_st_command_ban)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** DETACH **********************************/

SILC_FSM_STATE(silc_server_st_command_detach)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** WATCH ***********************************/

SILC_FSM_STATE(silc_server_st_command_watch)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************* SILCOPER *********************************/

SILC_FSM_STATE(silc_server_st_command_silcoper)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** LEAVE ***********************************/

SILC_FSM_STATE(silc_server_st_command_leave)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** USERS ***********************************/

SILC_FSM_STATE(silc_server_st_command_users)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** GETKEY **********************************/

SILC_FSM_STATE(silc_server_st_command_getkey)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}


/********************************** SERVICE *********************************/

SILC_FSM_STATE(silc_server_st_command_service)
{
  SilcServerThread thread = fsm_context;
  SilcServerCommand cmd = state_context;
  SilcArgumentPayload args = silc_command_get_args(cmd->payload);

  return SILC_FSM_FINISH;
}
