New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[silc.git] / apps / silcd / command.c
index a1e5e16c7d953022651e80ab7c22033429f60115..b4d5bcee37adf53669b7e5e77dde3ac515eb6dae 100644 (file)
@@ -35,7 +35,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len);
 static bool
 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
@@ -131,7 +131,6 @@ static int silc_server_is_registered(SilcServer server,
 
   silc_server_command_send_status_reply(cmd, command,
                                        SILC_STATUS_ERR_NOT_REGISTERED);
-  silc_server_command_free(cmd);
   return FALSE;
 }
 
@@ -149,6 +148,11 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
   SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
 
+  if (!client) {
+    silc_server_command_free(timeout->ctx);
+    silc_free(timeout);
+  }
+
   /* Update access time */
   client->last_command = time(NULL);
 
@@ -159,6 +163,8 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
                                     timeout->ctx, 
                                     timeout->cmd->cmd))
     timeout->cmd->cb(timeout->ctx, NULL);
+  else
+    silc_server_command_free(timeout->ctx);
 
   silc_free(timeout);
 }
@@ -233,15 +239,12 @@ void silc_server_command_process(SilcServer server,
                         silc_server_command_process_timeout,
                         (void *)timeout, 
                         2 - (time(NULL) - client->last_command), 0,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     else
       silc_schedule_task_add(server->schedule, sock->sock, 
                         silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                        (void *)timeout, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -251,6 +254,8 @@ void silc_server_command_process(SilcServer server,
     cmd->cb(ctx, NULL);
   else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
     cmd->cb(ctx, NULL);
+  else
+    silc_server_command_free(ctx);
 }
 
 /* Allocate Command Context */
@@ -297,24 +302,36 @@ silc_server_command_dup(SilcServerCommandContext ctx)
    with `context' when reply has been received.  It can be SILC_COMMAND_NONE
    to match any command with the `ident'.  If `ident' is non-zero
    the `callback' will be executed when received reply with command
-   identifier `ident'. */
+   identifier `ident'. If there already exists pending command for the
+   specified command, ident, callback and context this function has no
+   effect. */
 
-void silc_server_command_pending(SilcServer server,
+bool silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
                                 uint16 ident,
-                                SilcServerPendingDestructor destructor,
                                 SilcCommandCb callback,
                                 void *context)
 {
   SilcServerCommandPending *reply;
 
+  /* Check whether identical pending already exists for same command,
+     ident, callback and callback context. If it does then it would be
+     error to register it again. */
+  silc_dlist_start(server->pending_commands);
+  while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+       reply->callback == callback && reply->context == context)
+      return FALSE;
+  }
+
   reply = silc_calloc(1, sizeof(*reply));
   reply->reply_cmd = reply_cmd;
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
-  reply->destructor = destructor;
   silc_dlist_add(server->pending_commands, reply);
+
+  return TRUE;
 }
 
 /* Deletes pending command by reply command type. */
@@ -355,7 +372,6 @@ silc_server_command_pending_check(SilcServer server,
       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
       callbacks[i].context = r->context;
       callbacks[i].callback = r->callback;
-      callbacks[i].destructor = r->destructor;
       ctx->ident = ident;
       i++;
     }
@@ -365,14 +381,6 @@ silc_server_command_pending_check(SilcServer server,
   return callbacks;
 }
 
-/* Destructor function for pending callbacks. This is called when using
-   pending commands to free the context given for the pending command. */
-
-static void silc_server_command_destructor(void *context)
-{
-  silc_server_command_free((SilcServerCommandContext)context);
-}
-
 /* Sends simple status message as command reply packet */
 
 static void 
@@ -402,7 +410,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len)
 {
   SilcBuffer buffer;
@@ -495,6 +503,8 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
     (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
     if ((*client_id)[0] == NULL) {
       silc_free(*client_id);
+      silc_server_command_send_status_reply(cmd, command,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       return FALSE;
     }
     *client_id_count = 1;
@@ -512,6 +522,9 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
            for (i = 0; i < *client_id_count; i++)
              silc_free((*client_id)[i]);
            silc_free(*client_id);
+           silc_server_command_send_status_reply(
+                                        cmd, command,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            return FALSE;
          }
          (*client_id_count)++;
@@ -553,6 +566,8 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
   int i, k;
   bool no_res = TRUE;
 
+  SILC_LOG_DEBUG(("Start"));
+
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
     if (!entry)
@@ -579,7 +594,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
         to the command reply and we're done with this one. */
       silc_server_command_pending(server, SILC_COMMAND_NONE, 
                                  entry->resolve_cmd_ident,
-                                 silc_server_command_destructor,
                                  silc_server_command_whois,
                                  silc_server_command_dup(cmd));
       no_res = FALSE;
@@ -662,7 +676,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
     /* Reprocess this packet after received reply */
     silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                r->ident,
-                               silc_server_command_destructor,
                                silc_server_command_whois,
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
@@ -684,11 +697,13 @@ static void
 silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
                                     SilcClientEntry *clients,
                                     uint32 clients_count,
-                                    int count)
+                                    int count,
+                                    const char *nickname,
+                                    SilcClientID **client_ids)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
@@ -698,71 +713,49 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   unsigned char *fingerprint;
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
+  /* Process only valid clients and ignore those that are not registered. */
+  valid_count = 0;
+  for (i = 0; i < clients_count; i++) {
     if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      len++;
+      valid_count++;
+    else
+      clients[i] = NULL;
+  }
 
-  if (len == 0 && clients_count) {
-    entry = clients[0];
-    if (entry->nickname) {
+  if (!valid_count) {
+    /* No valid clients found, send error reply */
+    if (nickname) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, entry->nickname, 
-                                          strlen(entry->nickname));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+                                          3, nickname, strlen(nickname));
+    } else if (client_ids && client_ids[0]) {
+      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
                                           2, idp->data, idp->len);
       silc_buffer_free(idp);
     }
-
     return;
   }
 
-  status = SILC_STATUS_OK;
-  if (len > 1)
+  /* Start processing found clients. */
+  if (valid_count > 1)
     status = SILC_STATUS_LIST_START;
+  else
+    status = SILC_STATUS_OK;
 
   for (i = 0, k = 0; i < clients_count; i++) {
     entry = clients[i];
-
-    if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      if (clients_count == 1) {
-       if (entry->nickname) {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                              SILC_STATUS_ERR_NO_SUCH_NICK,
-                                              3, entry->nickname, 
-                                              strlen(entry->nickname));
-       } else {
-         SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, idp->data, idp->len);
-         silc_buffer_free(idp);
-       }
-      }
+    if (!entry)
       continue;
-    }
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
+    if (valid_count > 1 && k == valid_count - 1)
       status = SILC_STATUS_LIST_END;
-
     if (count && k - 1 == count)
       status = SILC_STATUS_LIST_END;
 
-    if (count && k - 1 > count)
-      break;
-
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    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);
@@ -832,6 +825,33 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   }
 }
 
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
 static int
 silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
@@ -854,34 +874,10 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
      Since nicknames can be expanded into many clients we need to send it
      to router.  If the WHOIS included only client ID's we will check them
      first locally since we just might have them. */
-  if (nick && !client_id_count &&
-      cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending && 
+  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending &&
       !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send WHOIS command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_whois,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
+    silc_server_command_whois_send_router(cmd);
     ret = -1;
     goto out;
   }
@@ -904,9 +900,20 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
        clients = silc_realloc(clients, sizeof(*clients) * 
                               (clients_count + 1));
        clients[clients_count++] = entry;
+      } else {
+       /* If we are normal server and did not send the request first to router
+          do it now, since we do not have the Client ID information. */
+       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+           server->server_type == SILC_SERVER && !cmd->pending && 
+           !server->standalone) {
+         silc_server_command_whois_send_router(cmd);
+         ret = -1;
+         goto out;
+       }
       }
     }
   } else {
+    /* Find by nickname */
     if (!silc_idlist_get_clients_by_hash(server->local_list, 
                                         nick, server->md5hash,
                                         &clients, &clients_count))
@@ -924,6 +931,16 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
   }
   
   if (!clients) {
+    /* If we are normal server and did not send the request first to router
+       do it now, since we do not have the information. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_whois_send_router(cmd);
+      ret = -1;
+      goto out;
+    }
+
     /* Such client(s) really does not exist in the SILC network. */
     if (!client_id_count) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
@@ -951,7 +968,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
+                                      count, nick, client_id);
 
  out:
   if (client_id_count) {
@@ -977,9 +994,7 @@ SILC_SERVER_CMD_FUNC(whois)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
 
   ret = silc_server_command_whois_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
+  silc_server_command_free(cmd);
 }
 
 /******************************************************************************
@@ -1048,11 +1063,9 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_whowas, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
 
       silc_buffer_free(tmpbuf);
@@ -1070,49 +1083,57 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, count = 0, len;
+  int i, k, count = 0, len;
   SilcBuffer packet, idp;
   SilcClientEntry entry = NULL;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
-  char found = FALSE;
   char nh[256], uh[256];
+  int valid_count;
 
   status = SILC_STATUS_OK;
-  if (clients_count > 1)
-    status = SILC_STATUS_LIST_START;
 
+  /* Process only entries that are not registered anymore. */
+  valid_count = 0;
   for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      clients[i] = NULL;
+    else
+      valid_count++;
+  }
 
-    /* We will take only clients that are not valid anymore. They are the
-       ones that are not registered anymore but still have a ID. They
-       have disconnected us, and thus valid for WHOWAS. */
-    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      continue;
-    if (entry->id == NULL)
-      continue;
+  if (!valid_count) {
+    /* No valid entries found at all, just send error */
+    unsigned char *tmp;
+    
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp)
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, tmp, strlen(tmp));
+    return;
+  }
 
-    if (count && i - 1 == count)
-      break;
+  if (valid_count > 1)
+    status = SILC_STATUS_LIST_START;
 
-    found = TRUE;
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
 
-    if (clients_count > 2)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && i == clients_count - 1)
+    if (valid_count > 1 && k == valid_count - 1)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 == count)
       status = SILC_STATUS_LIST_END;
+    if (count && k - 1 > count)
+      break;
 
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username)
-      continue;
-      
     /* Send WHOWAS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
-    
     memset(uh, 0, sizeof(uh));
     memset(nh, 0, sizeof(nh));
 
@@ -1149,13 +1170,9 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
-  }
 
-  if (found == FALSE && entry)
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, entry->nickname, 
-                                        strlen(entry->nickname));
+    k++;
+  }
 }
 
 static int
@@ -1192,11 +1209,9 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
     /* Reprocess this packet after received reply from router */
     silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
                                silc_server_command_whowas,
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
-
     silc_command_set_ident(cmd->payload, old_ident);
 
     silc_buffer_free(tmpbuf);
@@ -1254,7 +1269,6 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(nick);
   silc_free(server_name);
-
   return ret;
 }
 
@@ -1268,9 +1282,7 @@ SILC_SERVER_CMD_FUNC(whowas)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 
   ret = silc_server_command_whowas_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
+  silc_server_command_free(cmd);
 }
 
 /******************************************************************************
@@ -1279,7 +1291,34 @@ SILC_SERVER_CMD_FUNC(whowas)
 
 ******************************************************************************/
 
-static bool
+static void 
+silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send IDENTIFY command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_identify,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
+static int
 silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   SilcClientEntry **clients,
                                   uint32 *clients_count,
@@ -1287,8 +1326,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   uint32 *servers_count,
                                   SilcChannelEntry **channels,
                                   uint32 *channels_count,
-                                  uint32 *count,
-                                  bool *names)
+                                  uint32 *count)
 {
   SilcServer server = cmd->server;
   unsigned char *tmp;
@@ -1309,7 +1347,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
   if (!tmp) {
     /* No ID, get the names. */
-    *names = TRUE;
+
+    /* If we are normal server and have not resolved information from
+       router yet, do so now. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_identify_send_router(cmd);
+      return -1;
+    }
 
     /* Try to get nickname@server. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -1338,10 +1384,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       silc_free(nick_server);
 
       if (!(*clients)) {
+       /* the nickname does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1360,10 +1407,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*servers)) {
+       /* the server does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_SERVER,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1382,22 +1430,22 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*channels)) {
+       /* The channel does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CHANNEL,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
     if (!(*clients) && !(*servers) && !(*channels)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
+      return 0;
     }
   } else {
     /* Command includes ID, we must use that.  Also check whether the command
        has more than one ID set - take them all. */
-    *names = FALSE;
 
     /* Take all ID's from the command packet */
     for (i = 0; i < argc; i++) {
@@ -1412,9 +1460,10 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        silc_free(*clients);
        silc_free(*servers);
        silc_free(*channels);
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return FALSE;
+       silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_IDENTIFY,
+                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       return 0;
       }
 
       id = silc_id_payload_get_id(idp);
@@ -1432,10 +1481,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*clients_count + 1));
          (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_IDENTIFY,
                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, tmp, len);
-         error = TRUE;
+                                       2, tmp, len);
+           error = TRUE;
+         }
        }
 
        break;
@@ -1451,10 +1513,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*servers_count + 1));
          (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
-                                              2, tmp, len);
-         error = TRUE;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
        
@@ -1469,10 +1544,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   (*channels_count + 1));
          (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
-                                              2, tmp, len);
-         error = TRUE;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
       }
@@ -1495,7 +1583,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   else
     *count = 0;
 
-  return TRUE;
+  return 1;
 }
 
 /* Checks that all mandatory fields in client entry are present. If not
@@ -1540,7 +1628,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
         to the command reply and we're done with this one. */
       silc_server_command_pending(server, SILC_COMMAND_NONE, 
                                  entry->resolve_cmd_ident,
-                                 silc_server_command_destructor,
                                  silc_server_command_identify,
                                  silc_server_command_dup(cmd));
       no_res = FALSE;
@@ -1623,7 +1710,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
     /* Reprocess this packet after received reply */
     silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                r->ident,
-                               silc_server_command_destructor,
                                silc_server_command_identify,
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
@@ -1652,7 +1738,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                                        int count)
 {
   SilcServer server = cmd->server;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
@@ -1664,62 +1750,58 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
   if (clients) {
     SilcClientEntry entry;
 
-    len = 0;
-    for (i = 0; i < clients_count; i++)
+    /* Process only valid entries. */
+    valid_count = 0;
+    for (i = 0; i < clients_count; i++) {
       if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       len++;
+       valid_count++;
+      else
+       clients[i] = NULL;
+    }
 
-    if (len == 0 && clients_count) {
-      entry = clients[0];
-      if (entry->nickname) {
+    if (!valid_count) {
+      /* No valid entries found at all, just send error */
+      unsigned char *tmp;
+
+      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      if (tmp) {
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, entry->nickname, 
-                                            strlen(entry->nickname));
+                                            3, tmp, strlen(tmp));
       } else {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, idp->data, idp->len);
-       silc_buffer_free(idp);
+                                            2, tmp, len);
       }
-      
       return;
     }
 
-    if (len > 1)
+    /* Process all valid client entries and send command replies */
+
+    if (valid_count > 1)
       status = SILC_STATUS_LIST_START;
 
     for (i = 0, k = 0; i < clients_count; i++) {
       entry = clients[i];
-      
-      if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-       if (clients_count == 1) {
-         SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, idp->data, idp->len);
-         silc_buffer_free(idp);
-       }
+      if (!entry)
        continue;
-      }
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (clients_count > 1 && k == clients_count - 1 
+      if (valid_count > 1 && k == valid_count - 1 
          && !servers_count && !channels_count)
        status = SILC_STATUS_LIST_END;
       if (count && k - 1 == count)
        status = SILC_STATUS_LIST_END;
       if (count && k - 1 > count)
        break;
-      
+
       /* Send IDENTIFY reply */
+
       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-      
       memset(uh, 0, sizeof(uh));
       memset(nh, 0, sizeof(nh));
-      
       strncat(nh, entry->nickname, strlen(entry->nickname));
       if (!strchr(entry->nickname, '@')) {
        strncat(nh, "@", 1);
@@ -1732,7 +1814,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                  server->server_name, len);
        }
       }
-      
+
       if (!entry->username) {
        packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
                                                      status, ident, 2,
@@ -1764,9 +1846,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (servers) {
     SilcServerEntry entry;
 
@@ -1804,9 +1883,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (channels) {
     SilcChannelEntry entry;
 
@@ -1848,57 +1924,22 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 static int
 silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
-  SilcServer server = cmd->server;
   uint32 count = 0;
   int ret = 0;
   SilcClientEntry *clients = NULL;
   SilcServerEntry *servers = NULL;
   SilcChannelEntry *channels = NULL;
   uint32 clients_count = 0, servers_count = 0, channels_count = 0;
-  bool names;
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd,
-                                         &clients, &clients_count,
-                                         &servers, &servers_count,
-                                         &channels, &channels_count,
-                                         &count, &names))
-    return 0;
-
-  /* Send the IDENTIFY request to the router only if it included nickname.
-     Since nicknames can be expanded into many clients we need to send it
-     to router.  If the IDENTIFY included only client ID's we will check them
-     first locally since we just might have them. */
-  if (names && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send IDENTIFY command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_identify,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
-    ret = -1;
-    goto out;
-  }
+  ret = silc_server_command_identify_parse(cmd,
+                                          &clients, &clients_count,
+                                          &servers, &servers_count,
+                                          &channels, &channels_count,
+                                          &count);
+  if (ret < 1)
+    return ret;
+  ret = 0;
 
   /* Check that all mandatory fields are present and request those data
      from the server who owns the client if necessary. */
@@ -1919,7 +1960,6 @@ silc_server_command_identify_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(servers);
   silc_free(channels);
-
   return ret;
 }
 
@@ -1931,28 +1971,7 @@ SILC_SERVER_CMD_FUNC(identify)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 
   ret = silc_server_command_identify_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
-}
-
-/* Checks string for bad characters and returns TRUE if they are found. */
-
-static int silc_server_command_bad_chars(char *nick)
-{
-  int i;
-
-  for (i = 0; i < strlen(nick); i++) {
-    if (!isascii(nick[i]))
-      return TRUE;
-    if (nick[i] <= 32) return TRUE;
-    if (nick[i] == ' ') return TRUE;
-    if (nick[i] == '*') return TRUE;
-    if (nick[i] == '?') return TRUE;
-    if (nick[i] == ',') return TRUE;
-  }
-
-  return FALSE;
+  silc_server_command_free(cmd);
 }
 
 /* Server side of command NICK. Sets nickname for user. Setting
@@ -1966,6 +1985,7 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcServer server = cmd->server;
   SilcBuffer packet, nidp, oidp = NULL;
   SilcClientID *new_id;
+  uint32 nick_len;
   char *nick;
   uint16 ident = silc_command_get_ident(cmd->payload);
   int nickfail = 0;
@@ -1976,16 +1996,15 @@ SILC_SERVER_CMD_FUNC(nick)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (nick_len > 128)
+    nick[128] = '\0';
+  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
                                          SILC_STATUS_ERR_BAD_NICKNAME);
     goto out;
   }
 
-  if (strlen(nick) > 128)
-    nick[128] = '\0';
-
   /* Check for same nickname */
   if (!strcmp(client->nickname, nick)) {
     nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
@@ -2062,7 +2081,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                                    SilcChannelEntry *gch,
                                    uint32 gch_count)
 {
-  int i;
+  int i, k;
   SilcBuffer packet, idp;
   SilcChannelEntry entry;
   SilcCommandStatus status;
@@ -2070,27 +2089,34 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
   char *topic;
   unsigned char usercount[4];
   uint32 users;
+  int valid_lcount = 0, valid_rcount = 0;
 
-  for (i = 0; i < lch_count; i++)
+  for (i = 0; i < lch_count; i++) {
     if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       lch[i] = NULL;
-  for (i = 0; i < gch_count; i++)
+    else
+      valid_lcount++;
+  }
+  for (i = 0; i < gch_count; i++) {
     if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       gch[i] = NULL;
+    else
+      valid_rcount++;
+  }
 
   status = SILC_STATUS_OK;
   if ((lch_count + gch_count) > 1)
     status = SILC_STATUS_LIST_START;
 
   /* Local list */
-  for (i = 0; i < lch_count; i++) {
+  for (i = 0, k = 0; i < lch_count; i++) {
     entry = lch[i];
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (i >= 1 && i == lch_count - 1 && !gch_count)
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2118,17 +2144,18 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 
   /* Global list */
-  for (i = 0; i < gch_count; i++) {
+  for (i = 0, k = 0; i < gch_count; i++) {
     entry = gch[i];
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (i >= 1 && i == gch_count - 1)
+    if (valid_rcount > 1 && k == valid_rcount - 1)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2156,6 +2183,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 }
 
@@ -2191,13 +2219,12 @@ SILC_SERVER_CMD_FUNC(list)
     /* Reprocess this packet after received reply from router */
     silc_server_command_pending(server, SILC_COMMAND_LIST, 
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
                                silc_server_command_list, 
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
     silc_command_set_ident(cmd->payload, old_ident);
     silc_buffer_free(tmpbuf);
-    return;
+    goto out;
   }
 
   /* Get Channel ID */
@@ -2323,7 +2350,7 @@ SILC_SERVER_CMD_FUNC(topic)
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, TRUE, 
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -2420,6 +2447,7 @@ SILC_SERVER_CMD_FUNC(invite)
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (tmp) {
     char invite[512];
+    bool resolve;
 
     dest_id = silc_id_payload_parse_id(tmp, len);
     if (!dest_id) {
@@ -2429,11 +2457,12 @@ SILC_SERVER_CMD_FUNC(invite)
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id);
+    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
     if (!dest) {
-      if (server->server_type != SILC_SERVER) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      if (server->server_type != SILC_SERVER || !resolve) {
+       silc_server_command_send_status_reply(
+                                       cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
       }
       
@@ -2441,13 +2470,12 @@ SILC_SERVER_CMD_FUNC(invite)
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                  server->cmd_ident,
-                                 silc_server_command_destructor,
                                  silc_server_command_invite, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_free(channel_id);
       silc_free(dest_id);
-      return;
+      goto out;
     }
 
     /* Check whether the requested client is already on the channel. */
@@ -2647,6 +2675,7 @@ SILC_SERVER_CMD_FUNC(kill)
   SilcClientID *client_id;
   unsigned char *tmp, *comment;
   uint32 tmp_len, tmp_len2;
+  bool local;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
 
@@ -2684,9 +2713,11 @@ SILC_SERVER_CMD_FUNC(kill)
   /* Get the client entry */
   remote_client = silc_idlist_find_client_by_id(server->local_list, 
                                                client_id, TRUE, NULL);
+  local = TRUE;
   if (!remote_client) {
     remote_client = silc_idlist_find_client_by_id(server->global_list, 
                                                  client_id, TRUE, NULL);
+    local = FALSE;
     if (!remote_client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
@@ -2740,9 +2771,17 @@ SILC_SERVER_CMD_FUNC(kill)
     silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
     silc_server_close_connection(server, sock);
   } else {
+    /* Update statistics */
+    if (remote_client->connection)
+      server->stat.my_clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
     /* Remove remote client */
-    if (!silc_idlist_del_client(server->global_list, remote_client))
-      silc_idlist_del_client(server->local_list, remote_client);
+    silc_idlist_del_client(local ? server->local_list :
+                          server->global_list, remote_client);
   }
 
  out:
@@ -2810,10 +2849,10 @@ SILC_SERVER_CMD_FUNC(info)
     memset(info_string, 0, sizeof(info_string));
     snprintf(info_string, sizeof(info_string), 
             "location: %s server: %s admin: %s <%s>",
-            server->config->admin_info->location,
-            server->config->admin_info->server_type,
-            server->config->admin_info->admin_name,
-            server->config->admin_info->admin_email);
+            server->config->server_info->location,
+            server->config->server_info->server_type,
+            server->config->server_info->admin,
+            server->config->server_info->email);
 
     server_info = info_string;
     entry = server->id_entry;
@@ -2845,13 +2884,12 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
@@ -2870,13 +2908,12 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
   }
 
@@ -2975,6 +3012,7 @@ static void silc_server_command_join_channel(SilcServer server,
   uint16 ident = silc_command_get_ident(cmd->payload);
   char check[512], check2[512];
   bool founder = FALSE;
+  bool resolve;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2985,19 +3023,26 @@ static void silc_server_command_join_channel(SilcServer server,
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     client = (SilcClientEntry)sock->user_data;
   } else {
-    client = silc_server_get_client_resolve(server, client_id);
+    client = silc_server_get_client_resolve(server, client_id, &resolve);
     if (!client) {
       if (cmd->pending)
        goto out;
 
+      if (!resolve) {
+       silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
       /* The client info is being resolved. Reprocess this packet after
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                                 server->cmd_ident, NULL,
+                                 server->cmd_ident,
                                  silc_server_command_join, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      return;
+      goto out;
     }
 
     cmd->pending = FALSE;
@@ -3072,8 +3117,7 @@ static void silc_server_command_join_channel(SilcServer server,
        username and/or hostname is in the ban list the access to the
        channel is denied. */
     if (channel->ban_list) {
-      if (!channel->ban_list ||
-         silc_string_match(channel->ban_list, check) ||
+      if (silc_string_match(channel->ban_list, check) ||
          silc_string_match(channel->ban_list, check2)) {
        silc_server_command_send_status_reply(
                                      cmd, SILC_COMMAND_JOIN,
@@ -3103,8 +3147,7 @@ static void silc_server_command_join_channel(SilcServer server,
     }
   
     if (!passphrase || !channel->passphrase ||
-        memcmp(channel->passphrase, passphrase,
-               strlen(channel->passphrase))) {
+        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                            SILC_STATUS_ERR_BAD_PASSWORD);
       goto out;
@@ -3222,24 +3265,24 @@ static void silc_server_command_join_channel(SilcServer server,
       /* Distribute the channel key to all backup routers. */
       silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
                              keyp->data, keyp->len, FALSE, TRUE);
+  }
 
-    /* If client became founder by providing correct founder auth data
-       notify the mode change to the channel. */
-    if (founder) {
-      SILC_PUT32_MSB(chl->mode, mode);
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
-                                        clidp->data, clidp->len,
-                                        mode, 4, clidp->data, clidp->len);
+  /* If client became founder by providing correct founder auth data
+     notify the mode change to the channel. */
+  if (founder) {
+    SILC_PUT32_MSB(chl->mode, mode);
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      clidp->data, clidp->len,
+                                      mode, 4, clidp->data, clidp->len);
       
-      /* Set CUMODE notify type to network */
-      if (!server->standalone)
-       silc_server_send_notify_cumode(server, server->router->connection,
-                                      server->server_type == SILC_ROUTER ? 
-                                      TRUE : FALSE, channel,
-                                      chl->mode, client->id, SILC_ID_CLIENT,
-                                      client->id);
-    }
+    /* Set CUMODE notify type to network */
+    if (!server->standalone)
+      silc_server_send_notify_cumode(server, server->router->connection,
+                                    server->server_type == SILC_ROUTER ? 
+                                    TRUE : FALSE, channel,
+                                    chl->mode, client->id, SILC_ID_CLIENT,
+                                    client->id);
   }
 
   silc_buffer_free(reply);
@@ -3279,10 +3322,10 @@ SILC_SERVER_CMD_FUNC(join)
   }
   channel_name = tmp;
 
-  if (strlen(channel_name) > 256)
+  if (tmp_len > 256)
     channel_name[255] = '\0';
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+  if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_BAD_CHANNEL);
     goto out;
@@ -3312,16 +3355,8 @@ SILC_SERVER_CMD_FUNC(join)
                                             channel_name, NULL);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
-    }
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
     if (!channel || channel->disabled) {
       /* Channel not found */
@@ -3332,8 +3367,9 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
          goto out;
        }
        
@@ -3370,11 +3406,12 @@ SILC_SERVER_CMD_FUNC(join)
          /* Reprocess this packet after received reply from router */
          silc_server_command_pending(server, SILC_COMMAND_JOIN, 
                                      silc_command_get_ident(cmd->payload),
-                                     silc_server_command_destructor,
                                      silc_server_command_join,
                                      silc_server_command_dup(cmd));
          cmd->pending = TRUE;
-         return;
+          silc_command_set_ident(cmd->payload, old_ident);
+         silc_buffer_free(tmpbuf);
+         goto out;
        }
        
        /* We are router and the channel does not seem exist so we will check
@@ -3433,11 +3470,17 @@ SILC_SERVER_CMD_FUNC(join)
   if (cmd->pending && context2) {
     SilcServerCommandReplyContext reply = 
       (SilcServerCommandReplyContext)context2;
+
     if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
       tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
       SILC_GET32_MSB(created, tmp);
-      create_key = FALSE;      /* Router returned the key already */
+      if (silc_argument_get_arg_type(reply->args, 7, NULL))
+       create_key = FALSE;     /* Router returned the key already */
     }
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+       !silc_hash_table_count(channel->user_list))
+      created = TRUE;
   }
 
   /* If the channel does not have global users and is also empty the client
@@ -3483,10 +3526,10 @@ SILC_SERVER_CMD_FUNC(motd)
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
 
-    if (server->config && server->config->motd && 
-       server->config->motd->motd_file) {
+    if (server->config && server->config->server_info &&
+       server->config->server_info->motd_file) {
       /* Send motd */
-      motd = silc_file_readfile(server->config->motd->motd_file, &motd_len);
+      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
       if (!motd)
        goto out;
       
@@ -3534,13 +3577,12 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
@@ -3559,13 +3601,12 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry) {
@@ -3631,9 +3672,14 @@ SILC_SERVER_CMD_FUNC(umode)
       goto out;
     }
   } else {
-    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
-      /* Remove the server operator rights */
+    /* Remove the server operator rights */
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
       client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+      if (client->connection)
+       server->stat.my_server_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.server_ops--;
+    }
   }
 
   if (mask & SILC_UMODE_ROUTER_OPERATOR) {
@@ -3644,9 +3690,14 @@ SILC_SERVER_CMD_FUNC(umode)
       goto out;
     }
   } else {
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
-      /* Remove the router operator rights */
+    /* Remove the router operator rights */
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
       client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+      if (client->connection)
+       server->stat.my_router_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.router_ops--;
+    }
   }
 
   if (mask & SILC_UMODE_GONE) {
@@ -3829,22 +3880,22 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* The mode is removed and we need to generate and distribute
         new channel key. Clients are not using private channel keys
         anymore after this. */
-
+      
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
        goto out;
-      
+       
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
-
+       
       cipher = channel->channel_key->cipher->name;
       hmac = (char *)silc_hmac_get_name(channel->hmac);
     }
   }
-  
+
   if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
     /* User limit is set on channel */
     uint32 user_limit;
@@ -4243,9 +4294,9 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* If the target client is founder, no one else can change their mode
      but themselves. */
-  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) {
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_YOU);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
     goto out;
   }
 
@@ -4504,7 +4555,7 @@ SILC_SERVER_CMD_FUNC(kick)
     silc_server_send_notify_kicked(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel,
-                                  target_client->id, comment);
+                                  target_client->id, client->id, comment);
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Re-generate channel key */
@@ -4532,7 +4583,7 @@ SILC_SERVER_CMD_FUNC(oper)
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
   uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcServerConfigSectionAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
@@ -4581,6 +4632,12 @@ SILC_SERVER_CMD_FUNC(oper)
   /* Client is now server operator */
   client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_server_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.server_ops++;
+
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
@@ -4604,7 +4661,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
   uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcServerConfigSectionAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
@@ -4659,6 +4716,12 @@ SILC_SERVER_CMD_FUNC(silcoper)
   /* Client is now router operator */
   client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_router_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.router_ops++;
+
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
@@ -4781,7 +4844,11 @@ SILC_SERVER_CMD_FUNC(ban)
   }
 
   /* Get entry to the channel user list */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
 
   /* The client must be at least channel operator. */
   if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
@@ -4842,7 +4909,7 @@ SILC_SERVER_CMD_FUNC(ban)
                                         2, id, id_len,
                                         3, channel->ban_list, 
                                         channel->ban_list ? 
-                                        strlen(channel->ban_list) - 1 : 0);
+                                        strlen(channel->ban_list) -1 : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -4918,7 +4985,7 @@ SILC_SERVER_CMD_FUNC(close)
     server->router = NULL;
     server->standalone = TRUE;
   }
-  silc_server_free_sock_user_data(server, sock);
+  silc_server_free_sock_user_data(server, sock, NULL);
   silc_server_close_connection(server, sock);
   
  out:
@@ -5105,15 +5172,13 @@ SILC_SERVER_CMD_FUNC(users)
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_USERS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_users,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, ident);
-      
       silc_buffer_free(tmpbuf);
       silc_free(id);
-      return;
+      goto out;
     }
 
     /* Check the global list as well. */
@@ -5196,6 +5261,7 @@ SILC_SERVER_CMD_FUNC(getkey)
   uint32 tmp_len, pklen;
   SilcBuffer pk = NULL;
   SilcIdType id_type;
+  SilcPublicKey public_key;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -5247,14 +5313,12 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!client) {
@@ -5266,11 +5330,12 @@ SILC_SERVER_CMD_FUNC(getkey)
     /* The client is locally connected, just get the public key and
        send it back. If they key does not exist then do not send it, 
        send just OK reply */
-    if (!client->data.public_key) {
+    public_key = client->data.public_key;
+    if (!public_key) {
       pkdata = NULL;
       pklen = 0;
     } else {
-      tmp = silc_pkcs_public_key_encode(client->data.public_key, &tmp_len);
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
       pk = silc_buffer_alloc(4 + tmp_len);
       silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
       silc_buffer_format(pk,
@@ -5313,14 +5378,12 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!server_entry) {
@@ -5330,12 +5393,14 @@ SILC_SERVER_CMD_FUNC(getkey)
     }
 
     /* If they key does not exist then do not send it, send just OK reply */
-    if (!server_entry->data.public_key) {
+    public_key = (!server_entry->data.public_key ? 
+                 (server_entry == server->id_entry ? server->public_key :
+                  NULL) : server_entry->data.public_key);
+    if (!public_key) {
       pkdata = NULL;
       pklen = 0;
     } else {
-      tmp = silc_pkcs_public_key_encode(server_entry->data.public_key, 
-                                       &tmp_len);
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
       pk = silc_buffer_alloc(4 + tmp_len);
       silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
       silc_buffer_format(pk,