updates.
[silc.git] / apps / silcd / command.c
index 0395e40d2910cb9756c57b627b51d363f99d5914..146c96a7efbcd1731217d5f3b16dbc15fedc5c62 100644 (file)
@@ -59,7 +59,7 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(restart, RESTART, 
@@ -150,6 +150,7 @@ void silc_server_command_process(SilcServer server,
 {
   SilcServerCommandContext ctx;
   SilcServerCommand *cmd;
+  SilcCommand command;
 
   /* Allocate command context. This must be free'd by the
      command routine receiving it. */
@@ -169,14 +170,16 @@ void silc_server_command_process(SilcServer server,
     return;
   }
   ctx->args = silc_command_get_args(ctx->payload);
-  
+
   /* Get the command */
+  command = silc_command_get(ctx->payload);
   for (cmd = silc_command_list; cmd->cb; cmd++)
-    if (cmd->cmd == silc_command_get(ctx->payload))
+    if (cmd->cmd == command)
       break;
 
   if (cmd == NULL) {
-    SILC_LOG_ERROR(("Unknown command, packet dropped"));
+    silc_server_command_send_status_reply(ctx, command,
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND);
     silc_server_command_free(ctx);
     return;
   }
@@ -351,7 +354,10 @@ silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  buffer = silc_command_reply_payload_encode_va(command, status, 0, 0);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        0);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
@@ -373,8 +379,10 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  buffer = silc_command_reply_payload_encode_va(command, status, 0, 1,
-                                               arg_type, arg, arg_len);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        1, arg_type, arg, arg_len);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
@@ -479,7 +487,7 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
 
-    if (!entry->nickname || !entry->username) {
+    if (!entry->nickname || !entry->username || !entry->userinfo) {
       SilcBuffer tmpbuf;
       unsigned short old_ident;
 
@@ -521,10 +529,13 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   SilcServer server = cmd->server;
   char *tmp;
   int i, count = 0, len;
-  SilcBuffer packet, idp;
+  SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
   unsigned short ident = silc_command_get_ident(cmd->payload);
+  char nh[128], uh[128];
+  unsigned char idle[4], mode[4];
+  SilcSocketConnection hsock;
 
   status = SILC_STATUS_OK;
   if (clients_count > 1)
@@ -553,56 +564,72 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 
     /* Sanity check, however these should never fail. However, as
        this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username)
+    if (!entry->nickname || !entry->username || !entry->userinfo)
       continue;
       
     /* Send WHOIS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
     
-    {
-      char nh[256], uh[256];
-      unsigned char idle[4];
-      SilcSocketConnection hsock;
-
-      memset(uh, 0, sizeof(uh));
-      memset(nh, 0, sizeof(nh));
-
-      strncat(nh, entry->nickname, strlen(entry->nickname));
-      if (!strchr(entry->nickname, '@')) {
-       strncat(nh, "@", 1);
-       len = entry->router ? strlen(entry->router->server_name) :
-         strlen(server->server_name);
-       strncat(nh, entry->router ? entry->router->server_name :
-               server->server_name, len);
-      }
+    memset(uh, 0, sizeof(uh));
+    memset(nh, 0, sizeof(nh));
+    memset(idle, 0, sizeof(idle));
+    
+    strncat(nh, entry->nickname, strlen(entry->nickname));
+    if (!strchr(entry->nickname, '@')) {
+      strncat(nh, "@", 1);
+      len = entry->router ? strlen(entry->router->server_name) :
+       strlen(server->server_name);
+      strncat(nh, entry->router ? entry->router->server_name :
+             server->server_name, len);
+    }
       
-      strncat(uh, entry->username, strlen(entry->username));
-      if (!strchr(entry->username, '@')) {
-       strncat(uh, "@", 1);
-       hsock = (SilcSocketConnection)entry->connection;
-       len = strlen(hsock->hostname);
-       strncat(uh, hsock->hostname, len);
-      }
+    strncat(uh, entry->username, strlen(entry->username));
+    if (!strchr(entry->username, '@')) {
+      strncat(uh, "@", 1);
+      hsock = (SilcSocketConnection)entry->connection;
+      len = strlen(hsock->hostname);
+      strncat(uh, hsock->hostname, len);
+    }
+
+    channels = silc_server_get_client_channel_list(server, entry);
       
+    SILC_PUT32_MSB(entry->mode, mode);
+
+    if (entry->connection) {
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-      
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                            status, ident, 5, 
-                                            2, idp->data, idp->len,
-                                            3, nh, strlen(nh),
-                                            4, uh, strlen(uh),
-                                            5, entry->userinfo, 
-                                            strlen(entry->userinfo),
-                                            7, idle, 4);
     }
+
+    if (channels)
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 7, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   6, channels->data,
+                                                   channels->len,
+                                                   7, mode, 4,
+                                                   8, idle, 4);
+    else
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                                   status, ident, 6, 
+                                                   2, idp->data, idp->len,
+                                                   3, nh, strlen(nh),
+                                                   4, uh, strlen(uh),
+                                                   5, entry->userinfo, 
+                                                   strlen(entry->userinfo),
+                                                   7, mode, 4,
+                                                   8, idle, 4);
     
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    if (channels)
+      silc_buffer_free(channels);
   }
 }
 
@@ -621,8 +648,8 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && 
-      !cmd->pending && !server->standalone) {
+  if (server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
     SilcBuffer tmpbuf;
     unsigned short old_ident;
 
@@ -1629,6 +1656,7 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcBuffer packet, nidp, oidp;
   SilcClientID *new_id;
   char *nick;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -1686,14 +1714,14 @@ SILC_SERVER_CMD_FUNC(nick)
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Send NICK_CHANGE notify to the client's channels */
-  silc_server_send_notify_on_channels(server, client, 
+  silc_server_send_notify_on_channels(server, NULL, client, 
                                      SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
                                      oidp->data, oidp->len, 
                                      nidp->data, nidp->len);
 
   /* Send the new Client ID as reply command back to client */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
-                                               SILC_STATUS_OK, 0, 1, 
+                                               SILC_STATUS_OK, ident, 1, 
                                                2, nidp->data, nidp->len);
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
@@ -1724,6 +1752,7 @@ SILC_SERVER_CMD_FUNC(topic)
   SilcBuffer packet, idp;
   unsigned char *tmp;
   unsigned int argc, tmp_len;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
@@ -1811,13 +1840,13 @@ SILC_SERVER_CMD_FUNC(topic)
   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
   if (channel->topic)
     packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, 0, 2, 
+                                                 SILC_STATUS_OK, ident, 2, 
                                                  2, idp->data, idp->len,
                                                  3, channel->topic, 
                                                  strlen(channel->topic));
   else
     packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, 0, 1, 
+                                                 SILC_STATUS_OK, ident, 1, 
                                                  2, idp->data, idp->len);
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
@@ -1965,7 +1994,7 @@ SILC_TASK_CALLBACK(silc_server_command_quit_cb)
   /* Free all client specific data, such as client entry and entires
      on channels this client may be on. */
   silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
-                              q->signoff);
+                              TRUE, q->signoff);
   q->sock->user_data = NULL;
 
   /* Close the connection on our side */
@@ -2010,8 +2039,120 @@ SILC_SERVER_CMD_FUNC(quit)
   silc_server_command_free(cmd);
 }
 
+/* Server side of command KILL. This command is used by router operator
+   to remove an client from the SILC Network temporarily. */
+
 SILC_SERVER_CMD_FUNC(kill)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry remote_client;
+  SilcClientID *client_id;
+  unsigned char *tmp, *comment;
+  unsigned int tmp_len, tmp_len2;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_KILL, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* KILL command works only on router */
+  if (server->server_type != SILC_ROUTER) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Check whether client has the permissions. */
+  if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get the client entry */
+  remote_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, NULL);
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, NULL);
+    if (!remote_client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
+  }
+
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+  if (tmp_len2 > 128)
+    comment = NULL;
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                       SILC_STATUS_OK);
+
+  /* Send the KILL notify packets. First send it to the channel, then
+     to our primary router and then directly to the client who is being
+     killed right now. */
+
+  /* Send KILLED notify to the channels. It is not sent to the client
+     as it will be sent differently destined directly to the client and not
+     to the channel. */
+  silc_server_send_notify_on_channels(server, remote_client, 
+                                     remote_client, SILC_NOTIFY_TYPE_KILLED,
+                                     comment ? 2 : 1,
+                                     tmp, tmp_len,
+                                     comment, comment ? tmp_len2 : 0);
+
+  /* Send KILLED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_killed(server, server->router->connection, TRUE,
+                                  remote_client->id, SILC_ID_CLIENT_LEN,
+                                  comment);
+
+  /* Send KILLED notify to the client directly */
+  silc_server_send_notify_killed(server, remote_client->connection ? 
+                                remote_client->connection : 
+                                remote_client->router->connection, FALSE,
+                                remote_client->id, SILC_ID_CLIENT_LEN,
+                                comment);
+
+  /* Remove the client from all channels. This generates new keys to the
+     channels as well. */
+  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
+                                  NULL, TRUE);
+
+  /* Remove the client entry, If it is locally connected then we will also
+     disconnect the client here */
+  if (remote_client->data.registered && remote_client->connection) {
+    /* Remove locally conneted client */
+    SilcSocketConnection sock = remote_client->connection;
+    silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
+    silc_server_close_connection(server, sock);
+  } else {
+    /* Remove remote client */
+    if (!silc_idlist_del_client(server->global_list, remote_client))
+      silc_idlist_del_client(server->local_list, remote_client);
+  }
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 /* Server side of command INFO. This sends information about us to 
@@ -2519,8 +2660,76 @@ SILC_SERVER_CMD_FUNC(motd)
   silc_server_command_free(cmd);
 }
 
+/* Server side of command UMODE. Client can use this command to set/unset
+   user mode. Client actually cannot set itself to be as server/router
+   operator so this can be used only to unset the modes. */
+
 SILC_SERVER_CMD_FUNC(umode)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcBuffer packet;
+  unsigned char *tmp_mask;
+  unsigned int mask;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_UMODE, cmd, 2, 2);
+
+  /* Get the client's mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp_mask) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  SILC_GET32_MSB(mask, tmp_mask);
+
+  /* 
+   * Change the mode 
+   */
+
+  if (mask & SILC_UMODE_SERVER_OPERATOR) {
+    /* Cannot operator mode */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_PERM_DENIED);
+    goto out;
+  } else {
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
+      /* Remove the server operator rights */
+      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+  }
+
+  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
+    /* Cannot operator mode */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_PERM_DENIED);
+    goto out;
+  } else {
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
+      /* Remove the router operator rights */
+      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+  }
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
+                                               SILC_STATUS_OK, ident, 1,
+                                               2, tmp_mask, 4);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 /* Checks that client has rights to add or remove channel modes. If any
@@ -2591,6 +2800,7 @@ SILC_SERVER_CMD_FUNC(cmode)
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
   unsigned int argc, mode_mask, tmp_len, tmp_len2;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2943,7 +3153,7 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, 0, 1,
+                                               SILC_STATUS_OK, ident, 1,
                                                2, tmp_mask, 4);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
@@ -2972,6 +3182,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
   unsigned int target_mask, sender_mask, tmp_len, tmp_ch_len;
   int notify = FALSE;
+  unsigned short ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 3);
 
@@ -3137,7 +3348,7 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, 0, 2,
+                                               SILC_STATUS_OK, ident, 2,
                                                2, tmp_mask, 4,
                                                3, tmp_id, tmp_len);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
@@ -3307,12 +3518,150 @@ SILC_SERVER_CMD_FUNC(kick)
   silc_server_command_free(cmd);
 }
 
+/* Server side of OPER command. Client uses this comand to obtain server
+   operator privileges to this server/router. */
+
 SILC_SERVER_CMD_FUNC(oper)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_OPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now server operator */
+  client->mode |= SILC_UMODE_SERVER_OPERATOR;
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+   operator privileges to this router. */
+
 SILC_SERVER_CMD_FUNC(silcoper)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  unsigned int tmp_len;
+  SilcServerConfigSectionAdminConnection *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now router operator */
+  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, SILC_ID_CLIENT_LEN,
+                                 client->mode);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
 /* Server side command of CONNECT. Connects us to the specified remote
@@ -3323,7 +3672,7 @@ SILC_SERVER_CMD_FUNC(connect)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *tmp;
+  unsigned char *tmp, *host;
   unsigned int tmp_len;
   unsigned int port = SILC_PORT;
 
@@ -3347,8 +3696,8 @@ SILC_SERVER_CMD_FUNC(connect)
   }
 
   /* Get the remote server */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
@@ -3360,7 +3709,7 @@ SILC_SERVER_CMD_FUNC(connect)
     SILC_GET32_MSB(port, tmp);
 
   /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, tmp, port);
+  silc_server_create_connection(server, host, port);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
@@ -3382,6 +3731,7 @@ SILC_SERVER_CMD_FUNC(close)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServerEntry server_entry;
+  SilcSocketConnection sock;
   unsigned char *tmp;
   unsigned int tmp_len;
   unsigned char *name;
@@ -3420,16 +3770,15 @@ SILC_SERVER_CMD_FUNC(close)
     goto out;
   }
 
-  /* Close the connection to the server */
-  silc_server_free_sock_user_data(server, server_entry->connection);
-  silc_server_disconnect_remote(server, server_entry->connection,
-                               "Server closed connection: "
-                               "Closed by operator");
-  
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
                                        SILC_STATUS_OK);
 
+  /* Close the connection to the server */
+  sock = (SilcSocketConnection)server_entry->connection;
+  silc_server_free_sock_user_data(server, sock);
+  silc_server_close_connection(server, sock);
+  
  out:
   silc_server_command_free(cmd);
 }
@@ -3455,13 +3804,14 @@ SILC_SERVER_CMD_FUNC(shutdown)
     goto out;
   }
 
-  /* Then, gracefully, or not, bring the server down. */
-  silc_server_stop(server);
-
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
                                        SILC_STATUS_OK);
 
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
  out:
   silc_server_command_free(cmd);
 }
@@ -3652,7 +4002,7 @@ SILC_SERVER_CMD_FUNC(users)
 
   /* Send reply */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
-                                               SILC_STATUS_OK, 0, 4,
+                                               SILC_STATUS_OK, ident, 4,
                                                2, channel_id, channel_id_len,
                                                3, lc, 4,
                                                4, client_id_list->data,