Merged silc_1_0_branch to trunk.
[silc.git] / apps / silcd / server_query.c
index b212a4fe53f23a7b81fd13a348cd3ef42fcc3820..a248600953631b6993644b96b3a47a42a334ee81 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  server_query.c 
+  server_query.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 Pekka Riikonen
+  Copyright (C) 2002 - 2005 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -18,8 +18,6 @@
 */
 /* $Id$ */
 
-/* XXX TODO Requested Attributes to WHOIS */
-
 #include "serverincludes.h"
 #include "server_internal.h"
 
@@ -44,19 +42,19 @@ typedef struct {
 typedef struct {
   void *id;                        /* ID */
   SilcIdType id_type;              /* ID type */
-  SilcUInt16 index;                /* Index to IDs */
-  unsigned int from_cmd : 1;               /* TRUE if `index' is from command args,
-                                      otherwise from query->ids */
+  unsigned int index : 15;         /* Index to IDs */
+  unsigned int type : 2;           /* 0 = take from query->ids, 1 = take
+                                      from args, 2 = no args in error. */
   unsigned int error : 7;          /* The actual error (SilcStatus) */
 } *SilcServerQueryError;
 
 /* Query session context */
 typedef struct {
   /* Queried data */
-  char *nickname;                  /* Queried nickname */
+  char *nickname;                  /* Queried nickname, normalized */
   char *nick_server;               /* Queried nickname's server */
-  char *server_name;               /* Queried server name */
-  char *channel_name;              /* Queried channel name */
+  char *server_name;               /* Queried server name, normalized */
+  char *channel_name;              /* Queried channel name, normalized */
   SilcServerQueryID ids;           /* Queried IDs */
   SilcUInt32 ids_count;                    /* number of queried IDs */
   SilcUInt32 reply_count;          /* Requested reply count */
@@ -82,7 +80,7 @@ void silc_server_query_send_error(SilcServer server,
                                  SilcStatus error, ...);
 void silc_server_query_add_error(SilcServer server,
                                 SilcServerQuery query,
-                                bool from_cmd,
+                                SilcUInt32 type,
                                 SilcUInt32 index,
                                 SilcStatus error);
 void silc_server_query_add_error_id(SilcServer server,
@@ -106,9 +104,9 @@ void silc_server_query_send_reply(SilcServer server,
                                  SilcUInt32 servers_count,
                                  SilcChannelEntry *channels,
                                  SilcUInt32 channels_count);
-unsigned char *silc_server_query_reply_attrs(SilcServer server,
-                                            SilcServerQuery query,
-                                            SilcUInt32 *attrs_len);
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry client_entry);
 
 /* Free the query context structure and all allocated resources. */
 
@@ -165,7 +163,7 @@ void silc_server_query_send_error(SilcServer server,
 
   /* Send the command reply with error */
   silc_server_send_command_reply(server, query->cmd->sock,
-                                query->querycmd, error, 0, 
+                                query->querycmd, error, 0,
                                 silc_command_get_ident(query->cmd->payload),
                                 argc, data_type, data, data_len);
   va_end(va);
@@ -175,11 +173,13 @@ void silc_server_query_send_error(SilcServer server,
    processing and this function can be used to add one error.  The
    `index' is the index to the command context which includes the argument
    which caused the error, or it is the index to query->ids, depending
-   on value of `from_cmd'. */
+   on value of `type'.  If `type' is 0 the index is to query->ids, if
+   it is 1 it is index to the command context arguments, and if it is
+   2 the index is ignored and no argument is included in the error. */
 
 void silc_server_query_add_error(SilcServer server,
                                 SilcServerQuery query,
-                                bool from_cmd,
+                                SilcUInt32 type,
                                 SilcUInt32 index,
                                 SilcStatus error)
 {
@@ -188,7 +188,7 @@ void silc_server_query_add_error(SilcServer server,
   if (!query->errors)
     return;
   query->errors[query->errors_count].index = index;
-  query->errors[query->errors_count].from_cmd = from_cmd;
+  query->errors[query->errors_count].type = type;
   query->errors[query->errors_count].error = error;
   query->errors[query->errors_count].id = NULL;
   query->errors[query->errors_count].id_type = 0;
@@ -208,7 +208,7 @@ void silc_server_query_add_error_id(SilcServer server,
   if (!query->errors)
     return;
   query->errors[query->errors_count].index = 0;
-  query->errors[query->errors_count].from_cmd = FALSE;
+  query->errors[query->errors_count].type = 0;
   query->errors[query->errors_count].error = error;
   query->errors[query->errors_count].id = silc_id_dup(id, id_type);
   query->errors[query->errors_count].id_type = id_type;
@@ -236,11 +236,15 @@ bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
   switch (querycmd) {
 
   case SILC_COMMAND_WHOIS:
-    /* If we are normal server and query contains nickname, send it
-       directly to router. */
-    if (server->server_type == SILC_SERVER && !server->standalone &&
+    /* If we are normal server and query contains nickname OR query
+       doesn't contain nickname or ids BUT attributes, send it to the
+       router */
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
        cmd->sock != SILC_PRIMARY_ROUTE(server) &&
-       silc_argument_get_arg_type(cmd->args, 1, NULL)) {
+        (silc_argument_get_arg_type(cmd->args, 1, NULL) ||
+        (!silc_argument_get_arg_type(cmd->args, 1, NULL) &&
+         !silc_argument_get_arg_type(cmd->args, 4, NULL) &&
+         silc_argument_get_arg_type(cmd->args, 3, NULL)))) {
       silc_server_query_send_router(server, query);
       return TRUE;
     }
@@ -294,7 +298,7 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
   old_ident = silc_command_get_ident(query->cmd->payload);
   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
-  silc_server_packet_send(server, 
+  silc_server_packet_send(server,
                          SILC_PRIMARY_ROUTE(server),
                          SILC_PACKET_COMMAND, 0,
                          tmpbuf->data, tmpbuf->len, TRUE);
@@ -319,6 +323,13 @@ void silc_server_query_send_router_reply(void *context, void *reply)
 
   SILC_LOG_DEBUG(("Received reply from router to query"));
 
+  /* If the original command caller has gone away, just stop. */
+  if (query->cmd->sock->users == 1) {
+    SILC_LOG_DEBUG(("Original command caller vanished"));
+    silc_server_query_free(query);
+    return;
+  }
+
   /* Check if router sent error reply */
   if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
     SilcBuffer buffer;
@@ -331,7 +342,7 @@ void silc_server_query_send_router_reply(void *context, void *reply)
                           silc_command_get_ident(query->cmd->payload));
     buffer = silc_command_payload_encode_payload(cmdr->payload);
     silc_server_packet_send(server, query->cmd->sock,
-                           SILC_PACKET_COMMAND_REPLY, 0, 
+                           SILC_PACKET_COMMAND_REPLY, 0,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(buffer);
     silc_server_query_free(query);
@@ -359,13 +370,27 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
   switch (query->querycmd) {
 
   case SILC_COMMAND_WHOIS:
+    /* Get requested attributes if set */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+    if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN) {
+      query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
+
+      /* When Requested Attributes is present we will assure that this
+        client cannot execute the WHOIS command too fast.  This would be
+        same as having SILC_CF_LAG_STRICT. */
+      if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+          cmd->sock->user_data)
+       ((SilcClientEntry)cmd->sock->user_data)->fast_command = 6;
+    }
+
     /* Get Client IDs if present. Take IDs always instead of nickname. */
     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
     if (!tmp) {
 
       /* Get nickname */
       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-      if (!tmp) {
+      if (!tmp && !query->attrs) {
+       /* No nickname, no ids and no attributes - send error */
        silc_server_query_send_error(server, query,
                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        silc_server_query_free(query);
@@ -373,14 +398,26 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       }
 
       /* Get the nickname@server string and parse it */
-      if (tmp_len > 128 ||
-         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+      if (tmp && ((tmp_len > 128) ||
+         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))) {
        silc_server_query_send_error(server, query,
                                     SILC_STATUS_ERR_BAD_NICKNAME, 0);
        silc_server_query_free(query);
        return;
       }
 
+      /* Check nickname */
+      tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                                 SILC_STRING_UTF8, 128, &tmp_len);
+      if (!tmp) {
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
+       silc_server_query_free(query);
+       return;
+      }
+      silc_free(query->nickname);
+      query->nickname = tmp;
+
     } else {
       /* Parse the IDs included in the query */
       query->ids = silc_calloc(argc, sizeof(*query->ids));
@@ -390,9 +427,9 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        if (!tmp)
          continue;
 
-       id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
-       if (!id) {
-         silc_server_query_add_error(server, query, TRUE, i + 4,
+       id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+       if (!id || id_type != SILC_ID_CLIENT) {
+         silc_server_query_add_error(server, query, 1, i + 4,
                                      SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
        }
@@ -409,6 +446,9 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
              for (i = 0; i < query->ids_count; i++)
                silc_free(query->ids[i].id);
              silc_free(query->ids);
+             query->ids = NULL;
+             query->ids_count = 0;
+             silc_free(id);
              return;
            }
          }
@@ -424,12 +464,7 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
     if (tmp && tmp_len == sizeof(SilcUInt32))
       SILC_GET32_MSB(query->reply_count, tmp);
-
-    /* Get requested attributes if set */
-    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp)
-      query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
-    break;
+   break;
 
   case SILC_COMMAND_WHOWAS:
     /* Get nickname */
@@ -450,6 +485,18 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
       return;
     }
 
+    /* Check nickname */
+    tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                               SILC_STRING_UTF8, 128, &tmp_len);
+    if (!tmp) {
+      silc_server_query_send_error(server, query,
+                                  SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      silc_server_query_free(query);
+      return;
+    }
+    silc_free(query->nickname);
+    query->nickname = tmp;
+
     /* Get the max count of reply messages allowed */
     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
     if (tmp && tmp_len == sizeof(SilcUInt32))
@@ -467,19 +514,58 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
        /* Get the nickname@server string and parse it */
        if (tmp_len > 128 ||
            !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
-         silc_server_query_add_error(server, query, TRUE, 1,
+         silc_server_query_add_error(server, query, 1, 1,
                                      SILC_STATUS_ERR_BAD_NICKNAME);
+
+       /* Check nickname */
+       tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                                   SILC_STRING_UTF8, 128, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
+         silc_server_query_free(query);
+         return;
+       }
+       silc_free(query->nickname);
+       query->nickname = tmp;
       }
 
       /* Try get server name */
       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-      if (tmp)
-       query->server_name = silc_memdup(tmp, tmp_len);
+      if (tmp) {
+       /* Check server name */
+       tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
+                                   256, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_SERVER, 0);
+         silc_server_query_free(query);
+         return;
+       }
+       query->server_name = tmp;
+      }
 
       /* Get channel name */
       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-      if (tmp && tmp_len <= 256)
-       query->channel_name = silc_memdup(tmp, tmp_len);
+      if (tmp && tmp_len <= 256) {
+       /* Check channel name */
+       tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
+                                   256, &tmp_len);
+       if (!tmp) {
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_CHANNEL, 0);
+         silc_server_query_free(query);
+         return;
+       }
+       query->channel_name = tmp;
+      }
+
+      if (!query->nickname && !query->server_name && !query->channel_name) {
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       silc_server_query_free(query);
+       return;
+      }
 
     } else {
       /* Parse the IDs included in the query */
@@ -492,25 +578,41 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
 
        id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
        if (!id) {
-         silc_server_query_add_error(server, query, TRUE, i + 5,
-                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+         silc_server_query_add_error(server, query, 1, i + 5,
+                                     SILC_STATUS_ERR_BAD_CLIENT_ID);
          continue;
        }
 
        /* Normal server must check whether this ID exist, and if not then
           send the query to router, unless done so already */
        if (server->server_type == SILC_SERVER && !query->resolved) {
-         if (!silc_idlist_find_client_by_id(server->local_list,
-                                            id, TRUE, NULL)) {
-           if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
-               !silc_idlist_find_client_by_id(server->global_list,
+         if (id_type == SILC_ID_CLIENT) {
+           if (!silc_idlist_find_client_by_id(server->local_list,
                                               id, TRUE, NULL)) {
-             silc_server_query_send_router(server, query);
-             for (i = 0; i < query->ids_count; i++)
-               silc_free(query->ids[i].id);
-             silc_free(query->ids);
-             return;
+             if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
+                 !silc_idlist_find_client_by_id(server->global_list,
+                                                id, TRUE, NULL)) {
+               silc_server_query_send_router(server, query);
+               for (i = 0; i < query->ids_count; i++)
+                 silc_free(query->ids[i].id);
+               silc_free(query->ids);
+               query->ids = NULL;
+               query->ids_count = 0;
+               silc_free(id);
+               return;
+             }
            }
+         } else {
+           /* For now all other ID's except Client ID's are explicitly
+              sent to router for resolving. */
+           silc_server_query_send_router(server, query);
+           for (i = 0; i < query->ids_count; i++)
+             silc_free(query->ids[i].id);
+           silc_free(query->ids);
+           query->ids = NULL;
+           query->ids_count = 0;
+           silc_free(id);
+           return;
          }
        }
 
@@ -531,6 +633,115 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query)
   silc_server_query_process(server, query, TRUE);
 }
 
+/* Context for holding clients searched by public key. */
+typedef struct {
+  SilcClientEntry **clients;
+  SilcUInt32 *clients_count;
+  bool found;
+} *SilcServerPublicKeyUser, SilcServerPublicKeyUserStruct;
+
+void silc_server_public_key_hash_foreach(void *key, void *context,
+                                         void *user_context)
+{
+  SilcServerPublicKeyUser uc = user_context;
+  SilcClientEntry entry = context;
+
+  /* Nothing was found, just return */
+  if (!context)
+    return;
+
+  uc->found = TRUE;
+
+  (*uc->clients) = silc_realloc((*uc->clients),
+                                sizeof((**uc->clients)) *
+                               ((*uc->clients_count) + 1));
+  (*uc->clients)[(*uc->clients_count)++] = entry;
+}
+
+/* If clients are set, limit the found clients using the attributes in
+   the query. If clients are not set, try to find some clients using
+   the attributes */
+
+void silc_server_query_check_attributes(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry **clients,
+                                        SilcUInt32 *clients_count) {
+  SilcClientEntry entry;
+  SilcAttributePayload attr;
+  SilcAttribute attribute;
+  SilcAttributeObjPk pk;
+  SilcPublicKey publickey;
+  int i;
+  bool found = FALSE, no_clients = FALSE;
+
+  /* If no clients were found, we only check the attributes
+     if the user wasn't searching for nickname/ids */
+  if (!(*clients)) {
+    no_clients = TRUE;
+    if (query->nickname || query->ids_count)
+      return;
+  }
+
+  silc_dlist_start(query->attrs);
+  while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+    attribute = silc_attribute_get_attribute(attr);
+    switch (attribute) {
+
+      case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+       SILC_LOG_DEBUG(("Finding clients by public key attribute"));
+
+       if (!silc_attribute_get_object(attr, &pk, sizeof(pk)))
+         continue;
+
+       if (!silc_pkcs_public_key_decode(pk.data, pk.data_len,
+                                        &publickey)) {
+         silc_free(pk.type);
+         silc_free(pk.data);
+         continue;
+       }
+
+       /* If no clients were set on calling this function, we
+          just search for clients, otherwise we try to limit
+          the clients */
+       if (no_clients) {
+         SilcServerPublicKeyUserStruct usercontext;
+
+         usercontext.clients = clients;
+         usercontext.clients_count = clients_count;
+         usercontext.found = FALSE;
+
+         silc_hash_table_find_foreach(server->pk_hash, publickey,
+                                      silc_server_public_key_hash_foreach,
+                                      &usercontext);
+
+         if (usercontext.found == TRUE)
+           found = TRUE;
+       } else {
+         for (i = 0; i < *clients_count; i++) {
+           entry = (*clients)[i];
+
+           if (!entry->data.public_key)
+             continue;
+
+           if (!silc_hash_table_find_by_context(server->pk_hash, publickey,
+                                                entry, NULL))
+             (*clients)[i] = NULL;
+           else
+             found = TRUE;
+         }
+       }
+       silc_free(pk.type);
+       silc_free(pk.data);
+       silc_pkcs_public_key_free(publickey);
+       break;
+    }
+  }
+
+  if (!found && !query->nickname && !query->ids)
+    silc_server_query_add_error(server, query, 2, 0,
+                                SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+}
+
 /* Processes the parsed query.  This does the actual finding of the
    queried information and prepares for sending reply to the original
    sender of the query command. */
@@ -559,27 +770,27 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
 
   if (query->nickname) {
     /* Get all clients matching nickname from local list */
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
+    if (!silc_idlist_get_clients_by_hash(server->local_list,
                                         query->nickname, server->md5hash,
                                         &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
+      silc_idlist_get_clients_by_nickname(server->local_list,
                                          query->nickname,
                                          query->nick_server,
                                          &clients, &clients_count);
 
     /* Check global list as well */
     if (check_global) {
-      if (!silc_idlist_get_clients_by_hash(server->global_list, 
+      if (!silc_idlist_get_clients_by_hash(server->global_list,
                                           query->nickname, server->md5hash,
                                           &clients, &clients_count))
-       silc_idlist_get_clients_by_nickname(server->global_list, 
+       silc_idlist_get_clients_by_nickname(server->global_list,
                                            query->nickname,
                                            query->nick_server,
                                            &clients, &clients_count);
     }
 
     if (!clients)
-      silc_server_query_add_error(server, query, TRUE, 1,
+      silc_server_query_add_error(server, query, 1, 1,
                                  SILC_STATUS_ERR_NO_SUCH_NICK);
   }
 
@@ -596,7 +807,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
 
     if (!servers)
-      silc_server_query_add_error(server, query, TRUE, 2,
+      silc_server_query_add_error(server, query, 1, 2,
                                  SILC_STATUS_ERR_NO_SUCH_SERVER);
   }
 
@@ -614,7 +825,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
 
     if (!channels)
-      silc_server_query_add_error(server, query, TRUE, 3,
+      silc_server_query_add_error(server, query, 1, 3,
                                  SILC_STATUS_ERR_NO_SUCH_CHANNEL);
   }
 
@@ -635,7 +846,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_client_by_id(server->global_list,
                                                id, TRUE, NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
          continue;
        }
@@ -653,7 +864,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_server_by_id(server->global_list,
                                                id, TRUE, NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
          continue;
        }
@@ -670,7 +881,7 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
          entry = silc_idlist_find_channel_by_id(server->global_list, id,
                                                 NULL);
        if (!entry) {
-         silc_server_query_add_error(server, query, FALSE, i,
+         silc_server_query_add_error(server, query, 0, i,
                                      SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
          continue;
        }
@@ -686,6 +897,15 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
     }
   }
 
+  /* Check the attributes to narrow down the search by using them. */
+  if (query->attrs)
+    silc_server_query_check_attributes(server, query, &clients,
+                                       &clients_count);
+
+  SILC_LOG_DEBUG(("Querying %d clients", clients_count));
+  SILC_LOG_DEBUG(("Querying %d servers", servers_count));
+  SILC_LOG_DEBUG(("Querying %d channels", channels_count));
+
   /* If nothing was found, then just send the errors */
   if (!clients && !channels && !servers) {
     silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
@@ -739,6 +959,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
        }
       }
 
+      /* Remove the NOATTR status periodically */
+      if (client_entry->data.status & SILC_IDLIST_STATUS_NOATTR &&
+         client_entry->updated + 600 < time(NULL))
+       client_entry->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+
       /* When requested attributes is present and local client is detached
         we cannot send the command to the client, we'll reply on behalf of
         the client instead. */
@@ -747,6 +972,11 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query,
           client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
        continue;
 
+      /* If attributes are present in query, and in the entry and we have
+        done resolvings already we don't need to resolve anymore */
+      if (query->resolved && query->attrs && client_entry->attrs)
+       continue;
+
       /* Resolve the detailed client information. If client is local we
         know that attributes were present and we will resolve directly
         from the client. Otherwise resolve from client's owner. */
@@ -855,6 +1085,21 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     for (i = 0; i < query->querylist_count; i++) {
       r = &query->querylist[i];
 
+      /* If Requested Attributes were present put them to this resolving */
+      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
+       len = r->argc + 1;
+       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
+       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
+       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
+
+       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+       if (tmp)
+         r->arg[r->argc] = silc_memdup(tmp, len);
+       r->arg_lens[r->argc] = len;
+       r->arg_types[r->argc] = 3;
+       r->argc++;
+      }
+
       /* Send WHOIS command */
       res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
                                            r->argc, r->arg, r->arg_lens,
@@ -904,7 +1149,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
     case SILC_COMMAND_WHOIS:
     case SILC_COMMAND_IDENTIFY:
       /* Take existing query context if exist for this connection */
-      for (i = 0; i < query->queries_count; i++)
+      for (i = 0; i < query->querylist_count; i++)
        if (query->querylist[i].sock == sock) {
          r = &query->querylist[i];
          break;
@@ -913,7 +1158,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
       if (!r) {
        /* Allocate new temp query list context */
        query->querylist = silc_realloc(query->querylist,
-                                       sizeof(*query->querylist) * 
+                                       sizeof(*query->querylist) *
                                        (query->querylist_count + 1));
        r = &query->querylist[query->querylist_count];
        query->querylist_count++;
@@ -924,21 +1169,6 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
          r->timeout = 3;
       }
 
-      /* If Requested Attributes were present put them to this resolving */
-      if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
-       len = r->argc + 1;
-       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
-       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
-       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
-
-       tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-       if (tmp)
-         r->arg[r->argc] = silc_memdup(tmp, len);
-       r->arg_lens[r->argc] = len;
-       r->arg_types[r->argc] = 3;
-       r->argc++;
-      }
-
       len = r->argc + 1;
       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
@@ -970,6 +1200,7 @@ void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
   client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
   client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
   client_entry->resolve_cmd_ident = ident;
+  client_entry->updated = time(NULL);
 
   /* Save the queried ID, which we will reprocess after we get this and
      all other queries back. */
@@ -1020,6 +1251,7 @@ void silc_server_query_resolve_reply(void *context, void *reply)
     id = &query->queries[i];
 
     if (error == SILC_STATUS_ERR_TIMEDOUT) {
+
       /* If timeout occurred for local entry when resolving attributes
         mark that this client doesn't support attributes in WHOIS. This
         assures we won't send the request again to the client. */
@@ -1028,8 +1260,22 @@ void silc_server_query_resolve_reply(void *context, void *reply)
                                                     id->id, TRUE, NULL);
        SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
                        silc_id_render(id->id, SILC_ID_CLIENT)));
-       if (client_entry && SILC_IS_LOCAL(client_entry))
+       if (client_entry && SILC_IS_LOCAL(client_entry)) {
          client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+          continue;
+       }
+      }
+
+      /* Remove the RESOLVING status from the client entry */
+      if (query->querycmd != SILC_COMMAND_WHOWAS) {
+       client_entry = silc_idlist_find_client_by_id(server->local_list,
+                                                    id->id, TRUE, NULL);
+       if (!client_entry)
+         client_entry = silc_idlist_find_client_by_id(server->global_list,
+                                                      id->id, TRUE, NULL);
+       if (client_entry)
+         client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
       }
     }
   }
@@ -1042,6 +1288,13 @@ void silc_server_query_resolve_reply(void *context, void *reply)
 
   SILC_LOG_DEBUG(("Reprocess the query"));
 
+  /* If the original command caller has gone away, just stop. */
+  if (query->cmd->sock->users == 1) {
+    SILC_LOG_DEBUG(("Original command caller vanished"));
+    silc_server_query_free(query);
+    return;
+  }
+
   /* We have received all queries.  Now re-search all information required
      to complete this query.  Reason we cannot save the values found in
      the first search is that SilcClientEntry, SilcServerEntry and
@@ -1075,10 +1328,14 @@ void silc_server_query_send_reply(SilcServer server,
   SilcUInt32 len;
   SilcBuffer idp;
   int i, k, valid_count;
-  char nh[256], uh[256];
+  char nh[384], uh[384];
   bool sent_reply = FALSE;
 
   SILC_LOG_DEBUG(("Sending reply to query"));
+  SILC_LOG_DEBUG(("Sending %d clients", clients_count));
+  SILC_LOG_DEBUG(("Sending %d servers", servers_count));
+  SILC_LOG_DEBUG(("Sending %d channels", channels_count));
+  SILC_LOG_DEBUG(("Sending %d errors", query->errors_count));
 
   status = SILC_STATUS_OK;
 
@@ -1090,6 +1347,9 @@ void silc_server_query_send_reply(SilcServer server,
     /* Mark all invalid entries */
     for (i = 0, valid_count = 0; i < clients_count; i++) {
       entry = clients[i];
+      if (!entry)
+       continue;
+
       switch (query->querycmd) {
       case SILC_COMMAND_WHOIS:
        if (!entry->nickname || !entry->username || !entry->userinfo ||
@@ -1142,7 +1402,7 @@ void silc_server_query_send_reply(SilcServer server,
 
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (valid_count > 1 && k == valid_count - 1 
+      if (valid_count > 1 && k == valid_count - 1
          && !servers_count && !channels_count && !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
@@ -1156,7 +1416,6 @@ void silc_server_query_send_reply(SilcServer server,
                       "      : "), entry->nickname));
 
       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-      memset(uh, 0, sizeof(uh));
       memset(nh, 0, sizeof(nh));
 
       silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
@@ -1173,16 +1432,19 @@ void silc_server_query_send_reply(SilcServer server,
                       server->server_name, len);
        }
       }
-      
+
       switch (query->querycmd) {
-       
+
       case SILC_COMMAND_WHOIS:
        {
          unsigned char idle[4], mode[4];
-         unsigned char *fingerprint, fempty[20], *attrs;
-         SilcBuffer channels, umode_list = NULL;
+         unsigned char *fingerprint, fempty[20], *attrs = NULL;
+         SilcBuffer channels, umode_list = NULL, tmpattrs = NULL;
 
          memset(fempty, 0, sizeof(fempty));
+         memset(idle, 0, sizeof(idle));
+         memset(uh, 0, sizeof(uh));
+
          silc_strncat(uh, sizeof(uh), entry->username,
                       strlen(entry->username));
          if (!strchr(entry->username, '@') && entry->connection) {
@@ -1194,11 +1456,11 @@ void silc_server_query_send_reply(SilcServer server,
 
          if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
            channels =
-             silc_server_get_client_channel_list(server, entry, FALSE, 
+             silc_server_get_client_channel_list(server, entry, FALSE,
                                                  FALSE, &umode_list);
          else
            channels =
-             silc_server_get_client_channel_list(server, entry, TRUE, 
+             silc_server_get_client_channel_list(server, entry, TRUE,
                                                  TRUE, &umode_list);
 
          if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
@@ -1212,22 +1474,25 @@ void silc_server_query_send_reply(SilcServer server,
 
          /* If Requested Attribute were present, and we do not have the
             attributes we will reply to them on behalf of the client. */
+         len = 0;
          if (query->attrs) {
-           if (!entry->attrs) {
-             attrs = silc_server_query_reply_attrs(server, query, &len);
-           } else {
-             attrs = entry->attrs;
-             len = entry->attrs_len;
+           if (!entry->attrs && SILC_IS_LOCAL(entry)) {
+             tmpattrs = silc_server_query_reply_attrs(server, query, entry);
+             entry->attrs = silc_buffer_steal(tmpattrs, &len);
+             entry->attrs_len = len;
+             silc_buffer_free(tmpattrs);
            }
+           attrs = entry->attrs;
+           len = entry->attrs_len;
          }
 
          /* Send command reply */
          silc_server_send_command_reply(server, cmd->sock, query->querycmd,
-                                        status, 0, ident, 9,
+                                        status, 0, ident, 10,
                                         2, idp->data, idp->len,
                                         3, nh, strlen(nh),
                                         4, uh, strlen(uh),
-                                        5, entry->userinfo, 
+                                        5, entry->userinfo,
                                         strlen(entry->userinfo),
                                         6, channels ? channels->data : NULL,
                                         channels ? channels->len : 0,
@@ -1237,12 +1502,18 @@ void silc_server_query_send_reply(SilcServer server,
                                         fingerprint ? 20 : 0,
                                         10, umode_list ? umode_list->data :
                                         NULL, umode_list ? umode_list->len :
-                                        0);
+                                        0, 11, attrs, len);
+
          sent_reply = TRUE;
 
-         /* For now we will delete Requested Attributes */
-         silc_free(entry->attrs);
-         entry->attrs = NULL;
+         /* For now we always delete Requested Attributes, unless the client
+            is detached, in which case we don't want to reconstruct the
+            same data everytime */
+         if (!(entry->mode & SILC_UMODE_DETACHED) &&
+             !(entry->data.status & SILC_IDLIST_STATUS_NOATTR)) {
+           silc_free(entry->attrs);
+           entry->attrs = NULL;
+         }
 
          if (channels)
            silc_buffer_free(channels);
@@ -1282,7 +1553,7 @@ void silc_server_query_send_reply(SilcServer server,
       case SILC_COMMAND_WHOWAS:
        silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
        if (!strchr(entry->username, '@'))
-         silc_strncat(uh, sizeof(uh), "@*private*", 10);
+         silc_strncat(uh, sizeof(uh), "@-private-", 10);
 
        /* Send command reply */
        silc_server_send_command_reply(server, cmd->sock, query->querycmd,
@@ -1290,8 +1561,8 @@ void silc_server_query_send_reply(SilcServer server,
                                       2, idp->data, idp->len,
                                       3, nh, strlen(nh),
                                       4, uh, strlen(uh),
-                                      5, entry->userinfo, 
-                                      entry->userinfo ? 
+                                      5, entry->userinfo,
+                                      entry->userinfo ?
                                       strlen(entry->userinfo) : 0);
        sent_reply = TRUE;
        break;
@@ -1308,9 +1579,14 @@ void silc_server_query_send_reply(SilcServer server,
       /* Not one valid entry was found, send error.  If nickname was used
         in query send error based on that, otherwise the query->errors
         already includes proper errors. */
-      if (query->nickname)
-       silc_server_query_add_error(server, query, TRUE, 1,
+      if (query->nickname || (!query->ids && query->attrs))
+       silc_server_query_add_error(server, query, 1, 1,
                                    SILC_STATUS_ERR_NO_SUCH_NICK);
+
+      /* Make sure some error is sent */
+      if (!query->errors_count && !servers_count && !channels_count)
+       silc_server_query_add_error(server, query, 2, 0,
+                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     }
   }
 
@@ -1324,15 +1600,18 @@ void silc_server_query_send_reply(SilcServer server,
     k = 0;
     for (i = 0; i < servers_count; i++) {
       entry = servers[i];
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (servers_count == 1 && status != SILC_STATUS_OK && !channels_count &&
+         !query->errors_count)
+       status = SILC_STATUS_LIST_END;
       if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
          !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
        status = SILC_STATUS_LIST_END;
-      
+
       SILC_LOG_DEBUG(("%s: server %s",
                      (status == SILC_STATUS_OK ?         "   OK" :
                       status == SILC_STATUS_LIST_START ? "START" :
@@ -1345,13 +1624,13 @@ void silc_server_query_send_reply(SilcServer server,
       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
                                     status, 0, ident, 2,
-                                    2, idp->data, idp->len, 
-                                    3, entry->server_name, 
-                                    entry->server_name ? 
+                                    2, idp->data, idp->len,
+                                    3, entry->server_name,
+                                    entry->server_name ?
                                     strlen(entry->server_name) : 0);
       silc_buffer_free(idp);
       sent_reply = TRUE;
-      
+
       if (status == SILC_STATUS_LIST_END)
        break;
       k++;
@@ -1368,15 +1647,18 @@ void silc_server_query_send_reply(SilcServer server,
     k = 0;
     for (i = 0; i < channels_count; i++) {
       entry = channels[i];
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (channels_count == 1 && status != SILC_STATUS_OK &&
+         !query->errors_count)
+       status = SILC_STATUS_LIST_END;
       if (channels_count > 1 && k == channels_count - 1 &&
          !query->errors_count)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
        status = SILC_STATUS_LIST_END;
-      
+
       SILC_LOG_DEBUG(("%s: channel %s",
                      (status == SILC_STATUS_OK ?         "   OK" :
                       status == SILC_STATUS_LIST_START ? "START" :
@@ -1389,13 +1671,13 @@ void silc_server_query_send_reply(SilcServer server,
       idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
                                     status, 0, ident, 2,
-                                    2, idp->data, idp->len, 
-                                    3, entry->channel_name, 
-                                    entry->channel_name ? 
+                                    2, idp->data, idp->len,
+                                    3, entry->channel_name,
+                                    entry->channel_name ?
                                     strlen(entry->channel_name) : 0);
       silc_buffer_free(idp);
       sent_reply = TRUE;
-      
+
       if (status == SILC_STATUS_LIST_END)
        break;
       k++;
@@ -1414,14 +1696,19 @@ void silc_server_query_send_reply(SilcServer server,
       idp = NULL;
 
       /* Take error argument */
-      if (query->errors[i].from_cmd) {
+      if (query->errors[i].type == 1) {
+       /* Take from sent arguments */
+       len = 0;
        tmp = silc_argument_get_arg_type(cmd->args,
                                         query->errors[i].index, &len);
-       if (query->errors[i].index == 1)
-         type = 3;                 /* Nickname */
-       else
-         type = 2;                 /* ID */
+       type = 2;
+      } else if (query->errors[i].type == 2) {
+       /* No argument */
+       len = 0;
+       tmp = NULL;
+       type = 0;
       } else if (!query->errors[i].id) {
+       /* Take from query->ids */
        idp =
          silc_id_payload_encode(query->ids[query->errors[i].index].id,
                                 query->ids[query->errors[k].index].id_type);
@@ -1429,6 +1716,7 @@ void silc_server_query_send_reply(SilcServer server,
        len = idp->len;
        type = 2;
       } else {
+       /* Take added ID. */
        idp = silc_id_payload_encode(query->errors[i].id,
                                     query->errors[k].id_type);
        tmp = idp->data;
@@ -1438,6 +1726,8 @@ void silc_server_query_send_reply(SilcServer server,
 
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
+      if (query->errors_count == 1 && status != SILC_STATUS_OK)
+       status = SILC_STATUS_LIST_END;
       if (query->errors_count > 1 && k == query->errors_count - 1)
        status = SILC_STATUS_LIST_END;
       if (query->reply_count && k - 1 == query->reply_count)
@@ -1448,10 +1738,22 @@ void silc_server_query_send_reply(SilcServer server,
                       status == SILC_STATUS_LIST_START ? "START" :
                       status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
                       status == SILC_STATUS_LIST_END  ?  "  END" :
-                      "      : "), 
+                      "      : "),
                      silc_get_status_message(query->errors[i].error),
                      query->errors[i].error));
 
+#if 1 /* XXX Backwards compatibility.  Remove in 1.0. */
+      if (query->errors[i].error == SILC_STATUS_ERR_NO_SUCH_NICK)
+       /* Send error */
+       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
+                                      (status == SILC_STATUS_OK ?
+                                       query->errors[i].error : status),
+                                      (status == SILC_STATUS_OK ?
+                                       0 : query->errors[i].error), ident, 2,
+                                      type, tmp, len,
+                                      3, tmp, len);
+      else
+#endif
       /* Send error */
       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
                                     (status == SILC_STATUS_OK ?
@@ -1459,6 +1761,7 @@ void silc_server_query_send_reply(SilcServer server,
                                     (status == SILC_STATUS_OK ?
                                      0 : query->errors[i].error), ident, 1,
                                     type, tmp, len);
+
       silc_buffer_free(idp);
       sent_reply = TRUE;
 
@@ -1480,19 +1783,151 @@ void silc_server_query_send_reply(SilcServer server,
    Either client does not support Requested Attributes or isn't replying
    to them like it should. */
 
-unsigned char *silc_server_query_reply_attrs(SilcServer server,
-                                            SilcServerQuery query,
-                                            SilcUInt32 *attrs_len)
+SilcBuffer silc_server_query_reply_attrs(SilcServer server,
+                                        SilcServerQuery query,
+                                        SilcClientEntry client_entry)
 {
-  unsigned char *attrs = NULL;
-  SilcUInt32 len = 0;
+  SilcBuffer buffer = NULL;
+  SilcAttribute attribute;
+  SilcAttributePayload attr;
+  SilcAttributeObjPk pk;
+  SilcAttributeObjService service;
+  unsigned char *tmp;
+  unsigned char sign[2048 + 1];
+  SilcUInt32 sign_len;
 
   SILC_LOG_DEBUG(("Constructing Requested Attributes"));
 
-  if (attrs_len)
-    *attrs_len = len;
+  /* Go through all requested attributes */
+  silc_dlist_start(query->attrs);
+  while ((attr = silc_dlist_get(query->attrs)) != SILC_LIST_END) {
+    attribute = silc_attribute_get_attribute(attr);
+    switch (attribute) {
+
+    case SILC_ATTRIBUTE_SERVICE:
+      /* Put SERVICE.  Put only SILC service. */
+      memset(&service, 0, sizeof(service));
+      service.port = (server->config->server_info->primary ?
+                     server->config->server_info->primary->port : SILC_PORT);
+      silc_strncat(service.address, sizeof(service.address),
+                  server->server_name, strlen(server->server_name));
+      service.status = !(client_entry->mode & SILC_UMODE_DETACHED);
+      if (client_entry->connection)
+       service.idle = time(NULL) - client_entry->data.last_receive;
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            &service, sizeof(service));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_MOOD:
+      /* Put STATUS_MOOD */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            (void *)
+                                            SILC_ATTRIBUTE_MOOD_NORMAL,
+                                            sizeof(SilcUInt32));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_STATUS_FREETEXT:
+      /* Put STATUS_FREETEXT.  We just tell in the message that we are
+        replying on behalf of the client. */
+      tmp =
+       "This information was provided by the server on behalf of the user";
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            tmp, strlen(tmp));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_PREFERRED_CONTACT:
+      /* Put PREFERRED_CONTACT */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_VALID,
+                                            (void *)
+                                            SILC_ATTRIBUTE_CONTACT_CHAT,
+                                            sizeof(SilcUInt32));
+      if (!buffer)
+       return NULL;
+      break;
+
+    case SILC_ATTRIBUTE_USER_PUBLIC_KEY:
+      /* Put USER_PUBLIC_KEY */
+      if (client_entry->data.public_key) {
+       pk.type = "silc-rsa";
+       pk.data = silc_pkcs_public_key_encode(client_entry->data.public_key,
+                                             &pk.data_len);
+       buffer = silc_attribute_payload_encode(buffer, attribute, pk.data ?
+                                              SILC_ATTRIBUTE_FLAG_VALID :
+                                              SILC_ATTRIBUTE_FLAG_INVALID,
+                                              &pk, sizeof(pk));
+       silc_free(pk.data);
+       if (!buffer)
+         return NULL;
+       break;
+      }
+
+      /* No public key available */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_INVALID,
+                                            NULL, 0);
+      if (!buffer)
+       return NULL;
+      break;
+
+    default:
+      /* Ignore SERVER_PUBLIC_KEY since we are going to put it anyway later */
+      if (attribute == SILC_ATTRIBUTE_SERVER_PUBLIC_KEY ||
+         attribute == SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE)
+       break;
+
+      /* For other attributes we cannot reply so mark it invalid */
+      buffer = silc_attribute_payload_encode(buffer, attribute,
+                                            SILC_ATTRIBUTE_FLAG_INVALID,
+                                            NULL, 0);
+      if (!buffer)
+       return NULL;
+      break;
+    }
+  }
+
+  /* Always put our public key.  This assures that we send at least
+     something valid back always. */
+  pk.type = "silc-rsa";
+  pk.data = silc_pkcs_public_key_encode(server->public_key, &pk.data_len);
+  buffer = silc_attribute_payload_encode(buffer,
+                                        SILC_ATTRIBUTE_SERVER_PUBLIC_KEY,
+                                        pk.data ? SILC_ATTRIBUTE_FLAG_VALID :
+                                        SILC_ATTRIBUTE_FLAG_INVALID,
+                                        &pk, sizeof(pk));
+  silc_free(pk.data);
+  if (!buffer)
+    return NULL;
+
+  /* Finally compute the digital signature of all the data we provided
+     as an indication that we provided rightfull information, and this
+     also authenticates our public key. */
+  if (silc_pkcs_get_key_len(server->pkcs) / 8 <= sizeof(sign) -1  &&
+      silc_pkcs_sign_with_hash(server->pkcs, server->sha1hash,
+                              buffer->data, buffer->len,
+                              sign, &sign_len)) {
+    pk.type = NULL;
+    pk.data = sign;
+    pk.data_len = sign_len;
+    buffer =
+      silc_attribute_payload_encode(buffer,
+                                   SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE,
+                                   SILC_ATTRIBUTE_FLAG_VALID,
+                                   &pk, sizeof(pk));
+  }
+  if (!buffer)
+    return NULL;
 
-  return attrs;
+  return buffer;
 }
 
 /* Find client by the Client ID indicated by the `client_id', and if not