updates.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 29 Jun 2006 21:05:51 +0000 (21:05 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 29 Jun 2006 21:05:51 +0000 (21:05 +0000)
16 files changed:
includes/silc.h.in
lib/silccore/silcattrs.h
lib/silccore/silccommand.h
lib/silccore/silcidcache.c
lib/silccore/silcidcache.h
lib/silccore/silcnotify.h
lib/silcserver/server.c
lib/silcserver/server_entry.c
lib/silcserver/server_internal.h
lib/silcserver/server_send.c
lib/silcserver/server_st_accept.c
lib/silcserver/server_st_command.c
lib/silcserver/server_st_command.h
lib/silcserver/server_st_packet.c
lib/silcserver/server_st_query.c
lib/silcserver/server_util.c

index 20be333951b1f5ea4e2038839a3a991648b3beb3..3e0b2687347b16d836c7ed437dc41e806541bf50 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -258,6 +258,7 @@ extern "C" {
 /* More SILC util library includes */
 #include "silctime.h"
 #include "silcmutex.h"
+#include "silccond.h"
 #include "silcthread.h"
 #include "silcschedule.h"
 #include "silchashtable.h"
index b51ca8243b28447fe742a44b7b42db1cd5db36c5..a7b38d5e9c913674684d91e0e18003670f8046bc 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2005 Pekka Riikonen
+  Copyright (C) 2002 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -454,9 +454,7 @@ typedef struct SilcAttributeObjDeviceStruct {
  *    caller must free the data inside the structure.  The 'type' is one
  *    of following: "silc-rsa", "silc-dss, "ssh-rsa", "ssh-dss",
  *    "pgp-sign-rsa", "pgp-sign-dss", "x509v3-sign-rsa", "x509v3-sign-dss".
- *    See the draft-riikonen-precense-attrs draft for more detailed
- *    information.  The 'type' is NULL when this structure includes a
- *    digital signature.
+ *    The 'type' is NULL when this structure includes a digital signature.
  *
  *    In SILC, at least the "silc-rsa" must be supported.  In this case
  *    the key is normal SILC Public key.  To verify a signature with the
index 8c1094ff861d41248ccde10ba37c04038d24a913..eb0d176efce4540e4abd95160149d390be4d24b6 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -397,8 +397,8 @@ SilcUInt16 silc_command_get_ident(SilcCommandPayload payload);
  *
  ***/
 SilcBool silc_command_get_status(SilcCommandPayload payload,
-                            SilcStatus *status,
-                            SilcStatus *error);
+                                SilcStatus *status,
+                                SilcStatus *error);
 
 /****f* silccore/SilcCommandAPI/silc_command_set_ident
  *
index accb09176866d248e5fbc7a54e16443291c9928b..0d05b46e491eec04a91aeba0d5e39365cba76b64 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 2000 - 2005 Pekka Riikonen
+  Copyright (C) 2000 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -229,7 +229,12 @@ SilcBool silc_idcache_update(SilcIDCache cache, SilcIDCacheEntry entry,
     if (!silc_hash_table_del_by_context(cache->id_table, old_id, entry))
       return FALSE;
 
-    entry->id = new_id;
+    if (cache->id_type == SILC_ID_CLIENT)
+      *(SilcClientID *)entry->id = *(SilcClientID *)new_id;
+    if (cache->id_type == SILC_ID_SERVER)
+      *(SilcServerID *)entry->id = *(SilcServerID *)new_id;
+    if (cache->id_type == SILC_ID_CHANNEL)
+      *(SilcChannelID *)entry->id = *(SilcChannelID *)new_id;
 
     if (!silc_hash_table_add(cache->id_table, entry->id, entry))
       return FALSE;
index c48fe0b6dd08daca75c9f00b7d0cf296ea11cd9c..7b84e7bc01624c86b199ec6f05492b60ee5ab50e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2005 Pekka Riikonen
+  Copyright (C) 2000 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -208,10 +208,11 @@ SilcBool silc_idcache_del_by_context(SilcIDCache cache, void *context,
  * DESCRIPTION
  *
  *    Updates cache `entry' with new values.  If the `new_id' is non-NULL
- *    then the `entry' will be updated with `new_id'.  If the `new_name' is
- *    non-NULL then the `entry' will be updated with `new_name'.  The
- *    caller is responsible of freeing the old values that was added with
- *    silc_idcache_add.
+ *    then the new value will be copied over the old value in the `entry'.
+ *    If the `new_name' is non-NULL then the `entry' will be updated with
+ *    `new_name'.  The caller is responsible of freeing the old name if it
+ *    was updated with new one.  The old ID value does not need to be freed
+ *    as the new value is copied over the old value.
  *
  ***/
 SilcBool silc_idcache_update(SilcIDCache cache, SilcIDCacheEntry entry,
index 41b6d7c5aaba5cc2b0e46509d88ef5c029349b62..212a815a591cdacf0a1760146897ef2f3d80ee74 100644 (file)
@@ -1,15 +1,15 @@
 /*
+
   silcnotify.h
+
   Author: Pekka Riikonen <priikone@silcnet.org>
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+
+  Copyright (C) 1997 - 2006 Pekka Riikonen
+
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -33,7 +33,7 @@
 /****s* silccore/SilcNotifyAPI/SilcNotifyPayload
  *
  * NAME
- * 
+ *
  *    typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload;
  *
  * DESCRIPTION
@@ -49,7 +49,7 @@ typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload;
 /****d* silccore/SilcNotifyAPI/SilcNotifyType
  *
  * NAME
- * 
+ *
  *    typedef SilcUInt16 SilcNotifyType;
  *
  * DESCRIPTION
@@ -116,7 +116,7 @@ SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
  *    arguments must be {unsigned char *, SilcUInt32 (len)}.
  *
  ***/
-SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc, 
+SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
                                      va_list ap);
 
 /****f* silccore/SilcNotifyAPI/silc_notify_payload_encode_args
@@ -133,7 +133,7 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
  *    encoded Argument Payload buffer.
  *
  ***/
-SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, 
+SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type,
                                           SilcUInt32 argc,
                                           SilcBuffer args);
 
index 974e14d9572057cdf97b65ccd23d6a3420ec8499..756030e0e89a851e8d52868d01e60436b7b08be6 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -249,6 +249,7 @@ SILC_FSM_STATE(silc_server_thread_st_run)
     /* Each packet is processed in FSM thread */
     silc_list_start(thread->packet_queue);
     while ((packet = silc_list_get(thread->packet_queue)) != SILC_LIST_END) {
+      /* XXX shouldn't the fsm be &thread->fsm */
       t = silc_fsm_thread_alloc(fsm, thread, silc_server_thread_packet_dest,
                                NULL, FALSE);
       if (t) {
@@ -276,10 +277,10 @@ SILC_FSM_STATE(silc_server_thread_st_run)
     silc_list_start(thread->new_conns);
     while ((ac = silc_list_get(thread->new_conns)) != SILC_LIST_END) {
       ac->thread = thread;
-      ac->t = silc_fsm_thread_alloc(&thread->fsm, ac,
-                                   silc_server_accept_connection_dest,
-                                   NULL, FALSE);
-      silc_fsm_start(ac->t, silc_server_st_accept_connection);
+      silc_fsm_thread_init(&ac->t, &thread->fsm, ac,
+                          silc_server_accept_connection_dest,
+                          NULL, FALSE);
+      silc_fsm_start(&ac->t, silc_server_st_accept_connection);
     }
 
     /* Empty the list */
index 6effe759113a58506073f0a7ec91e12f76fa6169..da415ffb89cda9d022531e6e7d69252656974888 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
 #include "silcserver.h"
 #include "server_internal.h"
 
+/* XXX locking missing from routines! */
+
 /************************ Static utility functions **************************/
 
 /* Foreach callbcak to free all users from the channel when deleting a
@@ -241,7 +243,6 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id,
                              SilcServerID *new_id)
 {
   SilcIDCacheEntry id_cache = NULL;
-  SilcServerEntry entry;
 
   if (!old_id || !new_id)
     return NULL;
@@ -251,14 +252,9 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id,
   SILC_LOG_DEBUG(("New Server ID %s",
                  silc_id_render(new_id, SILC_ID_SERVER)));
 
-  if (!silc_idcache_find_by_id_one(server->servers, (void *)old_id,
-                                  &id_cache))
+  if (!silc_idcache_find_by_id_one(server->servers, old_id, &id_cache))
     return NULL;
-
-  entry = id_cache->context;
-  entry->id = *new_id;
-
-  if (!silc_idcache_update(server->servers, id_cache, old_id, &entry->id,
+  if (!silc_idcache_update(server->servers, id_cache, old_id, new_id,
                           NULL, NULL)) {
     SILC_LOG_ERROR(("Error updating Server ID"));
     return NULL;
@@ -266,7 +262,7 @@ silc_server_replace_server_id(SilcServer server, SilcServerID *old_id,
 
   SILC_LOG_DEBUG(("Replaced"));
 
-  return entry;
+  return id_cache->context;
 }
 
 
@@ -450,9 +446,9 @@ silc_server_replace_client_id(SilcServer server, SilcClientID *old_id,
     return NULL;
 
   SILC_LOG_DEBUG(("Replacing Client ID %s",
-                 silc_id_render(old_id, SILC_ID_SERVER)));
+                 silc_id_render(old_id, SILC_ID_CLIENT)));
   SILC_LOG_DEBUG(("New Client ID %s",
-                 silc_id_render(new_id, SILC_ID_SERVER)));
+                 silc_id_render(new_id, SILC_ID_CLIENT)));
 
   /* Normalize name. This is cached, original is in client context.  */
   if (nickname) {
@@ -462,15 +458,11 @@ silc_server_replace_client_id(SilcServer server, SilcClientID *old_id,
       return NULL;
   }
 
-  if (!silc_idcache_find_by_id_one(server->clients, (void *)old_id,
-                                  &id_cache))
+  if (!silc_idcache_find_by_id_one(server->clients, old_id, &id_cache))
     return NULL;
-
   entry = id_cache->context;
-  entry->id = *new_id;
-
   name = id_cache->name;
-  if (!silc_idcache_update(server->clients, id_cache, old_id, &entry->id,
+  if (!silc_idcache_update(server->clients, id_cache, old_id, new_id,
                           name, nicknamec)) {
     SILC_LOG_ERROR(("Error updating Client ID"));
     return NULL;
@@ -651,7 +643,6 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server,
                                                SilcChannelID *new_id)
 {
   SilcIDCacheEntry id_cache = NULL;
-  SilcChannelEntry entry;
 
   if (!old_id || !new_id)
     return NULL;
@@ -661,14 +652,9 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server,
   SILC_LOG_DEBUG(("New Channel ID %s",
                  silc_id_render(new_id, SILC_ID_CHANNEL)));
 
-  if (!silc_idcache_find_by_id_one(server->channels, (void *)old_id,
-                                  &id_cache))
+  if (!silc_idcache_find_by_id_one(server->channels, old_id, &id_cache))
     return NULL;
-
-  entry = id_cache->context;
-  entry->id = *new_id;
-
-  if (!silc_idcache_update(server->channels, id_cache, old_id, &entry->id,
+  if (!silc_idcache_update(server->channels, id_cache, old_id, new_id,
                           NULL, NULL)) {
     SILC_LOG_ERROR(("Error updating Channel ID"));
     return NULL;
@@ -676,7 +662,7 @@ SilcChannelEntry silc_server_replace_channel_id(SilcServer server,
 
   SILC_LOG_DEBUG(("Replaced"));
 
-  return entry;
+  return id_cache->context;
 }
 
 /* Returns channels from the ID list.  If the `channel_id' is NULL then
index 64a971b6ddd375fcc0acdb0ff59d03d77fef8428..80ffe34a3d754763185338dd0150a418c140b2a1 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -123,15 +123,12 @@ struct SilcClientEntryStruct {
   SilcUInt16 attrs_len;                     /* Attributes data length */
   SilcHashTable channels;           /* Joined channels */
   SilcPacketStream stream;          /* Connection to entry/origin of entry */
+  SilcUInt16 resolve_cmd_ident;             /* Command identifier when resolving */
 
   long last_command;
   SilcUInt8 fast_command;
   unsigned long updated;
 
-  /* data.status is RESOLVING and this includes the resolving command
-     reply identifier. */
-  SilcUInt16 resolve_cmd_ident;
-
   /* we need this so nobody can resume more than once at the same time -
    * server crashes, really odd behaviour, ... */
   SilcClientEntry resuming_client;
@@ -176,7 +173,7 @@ struct SilcChannelEntryStruct {
 typedef struct SilcServerAcceptStruct {
   SilcEntryDataStruct data;
   SilcServerThread thread;
-  SilcFSMThread t;                  /* Thread for accepting connection */
+  SilcFSMThreadStruct t;            /* Thread for accepting connection */
   SilcStream stream;                /* Remote connection */
   SilcPacketStream packet_stream;    /* Remote connection */
   SilcConnAuth connauth;            /* Connection authentication context */
@@ -357,6 +354,10 @@ struct SilcServerStruct {
 
 /* Macros */
 
+#define SILC_IS_CLIENT(entry) (entry->data.type == SILC_CONN_CLIENT)
+#define SILC_IS_SERVER(entry) (entry->data.type == SILC_CONN_SERVER)
+#define SILC_IS_ROUTER(entry) (entry->data.type == SILC_CONN_ROUTER)
+
 /* Return pointer to the primary router connection */
 #define SILC_PRIMARY_ROUTE(server) server->router
 
@@ -364,8 +365,10 @@ struct SilcServerStruct {
 #define SILC_BROADCAST(server) (server->server_type == SILC_ROUTER)
 
 /* Return TRUE if entry is locally connected or local to us */
-#define SILC_IS_LOCAL(entry) \
-  (((SilcIDListData)entry)->status & SILC_IDLIST_STATUS_LOCAL)
+#define SILC_IS_LOCAL(entry) (((SilcEntryData)entry)->local)
+
+/* Return TRUE if entry is registered */
+#define SILC_IS_REGISTERED(entry) (((SilcEntryData)entry)->registered)
 
 /* Registers generic task for file descriptor for reading from network and
    writing to network. As being generic task the actual task is allocated
@@ -453,19 +456,6 @@ do {                                                                       \
   silc_free(__fmt__);                                                  \
 } while(0)
 
-/* Connection retry timeout. We implement exponential backoff algorithm
-   in connection retry. The interval of timeout grows when retry count
-   grows. */
-#define SILC_SERVER_RETRY_COUNT        7        /* Max retry count */
-#define SILC_SERVER_RETRY_MULTIPLIER   2        /* Interval growth */
-#define SILC_SERVER_RETRY_RANDOMIZER   2        /* timeout += rnd % 2 */
-#define SILC_SERVER_RETRY_INTERVAL_MIN 10       /* Min retry timeout */
-#define SILC_SERVER_RETRY_INTERVAL_MAX 600      /* Max generated timeout */
-
-#define SILC_SERVER_KEEPALIVE          300      /* Heartbeat interval */
-#define SILC_SERVER_REKEY              3600     /* Session rekey interval */
-#define SILC_SERVER_MAX_CONNECTIONS    1000     /* Max connections */
-#define SILC_SERVER_MAX_CONNECTIONS_SINGLE 1000  /* Max connections per host */
 #define SILC_SERVER_LOG_FLUSH_DELAY    300       /* Default log flush delay */
 
 /* Macros */
index 9ec759d106af6cf1a12a5b21de627c15c59f511d..5f9a61abe0673b807c6d3995b6aa2a81a44ef954 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -85,6 +85,80 @@ SilcBool silc_server_send_new_id(SilcPacketStream stream,
 }
 
 
+/****************************** Command packets *****************************/
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct form in correct order, and they must as follows:
+   { argument type, argument data, argument length }. */
+
+SilcBool silc_server_send_command(SilcServer server,
+                                 SilcPacketStream stream,
+                                 SilcCommand command,
+                                 SilcUInt16 ident,
+                                 SilcUInt32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+  SilcBool ret = FALSE;
+
+  /* Statistics */
+  server->stat.commands_sent++;
+
+  va_start(ap, argc);
+
+  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
+  if (!packet) {
+    va_end(ap);
+    return ret;
+  }
+
+  ret = silc_packet_send(stream, SILC_PACKET_COMMAND, 0,
+                        packet->data, silc_buffer_len(packet));
+
+  silc_buffer_free(packet);
+  va_end(ap);
+
+  return ret;
+}
+
+/* Generic function to send a command reply.  The arguments must be sent
+   already encoded into correct form in correct order, and they must be
+   { argument type, argument data, argument length }. */
+
+SilcBool silc_server_send_command_reply(SilcServer server,
+                                       SilcPacketStream stream,
+                                       SilcCommand command,
+                                       SilcStatus status,
+                                       SilcStatus error,
+                                       SilcUInt16 ident,
+                                       SilcUInt32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+  SilcBool ret = FALSE;
+
+  /* Statistics */
+  server->stat.commands_sent++;
+
+  va_start(ap, argc);
+
+  packet = silc_command_reply_payload_encode_vap(command, status, error,
+                                                ident, argc, ap);
+  if (!packet) {
+    va_end(ap);
+    return ret;
+  }
+
+  ret = silc_packet_send(stream, SILC_PACKET_COMMAND_REPLY, 0,
+                        packet->data, silc_buffer_len(packet));
+
+  silc_buffer_free(packet);
+  va_end(ap);
+
+  return ret;
+}
+
+
 /****************************** Notify packets ******************************/
 
 /* Sends notify packet.  Each variable argument format in the argument list
index b6e638842840f9a830b97d0bf3ea45dc56de8835..07a83ecf1e0a248bd2476583849784157b62c551 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -66,7 +66,7 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
   ac->rekey = rekey;
 
   /* Continue synchronously to take keys into use immediately */
-  SILC_FSM_CALL_CONTINUE_SYNC(ac->t);
+  SILC_FSM_CALL_CONTINUE_SYNC(&ac->t);
 }
 
 /* Authentication data callback */
@@ -129,7 +129,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
 {
   SilcServerAccept ac = context;
   ac->auth_success = success;
-  SILC_FSM_CALL_CONTINUE(ac->t);
+  SILC_FSM_CALL_CONTINUE(&ac->t);
 }
 
 /* Free context */
@@ -146,7 +146,6 @@ void silc_server_accept_connection_dest(SilcFSM fsm, void *fsm_context,
                                        void *destructor_context)
 {
   SilcServerAccept ac = fsm_context;
-  silc_fsm_free(fsm);
   silc_server_accept_free(ac);
 }
 
index 70f5e2d0dcb9e62feca0db869e9f633abc96fce0..62779e084b0eda67b257eff212676e1039c7b302 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 /************************** Types and definitions ***************************/
 
-#define SILC_SERVER_COMMAND_CHECK(min, max)                                 \
-do {                                                                        \
-  SilcUInt32 _argc;                                                         \
-                                                                            \
-  SILC_LOG_DEBUG(("Start"));                                                \
-                                                                            \
-  _argc = silc_argument_get_arg_num(args);                                  \
-  if (_argc < min) {                                                        \
-    SILC_LOG_DEBUG(("Not enough parameters in command"));                   \
-    silc_server_command_send_status_reply(cmd,                              \
-                                         silc_command_get(cmd->payload),    \
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
-                                         0);                                \
-    silc_server_command_free(cmd);                                          \
-    return SILC_FSM_FINISH;                                                 \
-  }                                                                         \
-  if (_argc > max) {                                                        \
-    SILC_LOG_DEBUG(("Too many parameters in command"));                             \
-    silc_server_command_send_status_reply(cmd,                              \
-                                         silc_command_get(cmd->payload),    \
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS,   \
-                                         0);                                \
-    silc_server_command_free(cmd);                                          \
-    return SILC_FSM_FINISH;                                                 \
-  }                                                                         \
+#define SILC_SERVER_COMMAND_CHECK(min, max)                            \
+do {                                                                   \
+  SilcUInt32 _argc;                                                    \
+                                                                       \
+  SILC_LOG_DEBUG(("Start"));                                           \
+                                                                       \
+  _argc = silc_argument_get_arg_num(args);                             \
+  if (_argc < min) {                                                   \
+    SILC_LOG_DEBUG(("Not enough parameters in command"));              \
+    silc_server_command_status_reply(cmd,                              \
+                                    silc_command_get(cmd->payload),    \
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
+                                    0);                                \
+    silc_server_command_free(cmd);                                     \
+    return SILC_FSM_FINISH;                                            \
+  }                                                                    \
+  if (_argc > max) {                                                   \
+    SILC_LOG_DEBUG(("Too many parameters in command"));                        \
+    silc_server_command_status_reply(cmd,                              \
+                                    silc_command_get(cmd->payload),    \
+                                    SILC_STATUS_ERR_TOO_MANY_PARAMS,   \
+                                    0);                                \
+    silc_server_command_free(cmd);                                     \
+    return SILC_FSM_FINISH;                                            \
+  }                                                                    \
 } while(0)
 
 
@@ -56,7 +56,7 @@ do {                                                                       \
 /* Sends simple status message as command reply packet */
 
 static void
-silc_server_command_send_status_reply(SilcServerCommand cmd,
+silc_server_command_status_reply(SilcServerCommand cmd,
                                      SilcCommand command,
                                      SilcStatus status,
                                      SilcStatus error)
@@ -80,7 +80,7 @@ silc_server_command_send_status_reply(SilcServerCommand cmd,
    type must be sent as argument. */
 
 static void
-silc_server_command_send_status_data(SilcServerCommand cmd,
+silc_server_command_status_data(SilcServerCommand cmd,
                                     SilcCommand command,
                                     SilcStatus status,
                                     SilcStatus error,
@@ -105,7 +105,7 @@ silc_server_command_send_status_data(SilcServerCommand cmd,
 }
 
 static void
-silc_server_command_send_status_data2(SilcServerCommand cmd,
+silc_server_command_status_data2(SilcServerCommand cmd,
                                      SilcCommand command,
                                      SilcStatus status,
                                      SilcStatus error,
@@ -355,6 +355,8 @@ SILC_FSM_STATE(silc_server_st_packet_command)
       else
        client->fast_command -= 2;
     }
+
+    client->last_command = time(NULL) + timeout;
   }
 
   silc_fsm_set_state_context(fsm, cmd);
@@ -514,6 +516,53 @@ SILC_FSM_STATE(silc_server_st_packet_command)
   return timeout ? SILC_FSM_WAIT : SILC_FSM_CONTINUE;
 }
 
+/********************************* WHOIS ************************************/
+
+SILC_FSM_STATE(silc_server_st_command_whois)
+{
+  SilcServerCommand cmd = state_context;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(1, 256);
+
+  /** WHOIS query */
+  silc_fsm_next(fsm, silc_server_st_query_whois);
+
+  return SILC_FSM_CONTINUE;
+}
+
+
+/********************************* WHOWAS ***********************************/
+
+SILC_FSM_STATE(silc_server_st_command_whowas)
+{
+  SilcServerCommand cmd = state_context;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(1, 2);
+
+  /** WHOWAS query */
+  silc_fsm_next(fsm, silc_server_st_query_whowas);
+
+  return SILC_FSM_CONTINUE;
+}
+
+
+/******************************** IDENTIFY **********************************/
+
+SILC_FSM_STATE(silc_server_st_command_identify)
+{
+  SilcServerCommand cmd = state_context;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(1, 256);
+
+  /** IDENTIFY query */
+  silc_fsm_next(fsm, silc_server_st_query_identify);
+
+  return SILC_FSM_CONTINUE;
+}
+
 
 /********************************** NICK ************************************/
 
@@ -522,7 +571,99 @@ SILC_FSM_STATE(silc_server_st_command_nick)
   SilcServerThread thread = fsm_context;
   SilcServerCommand cmd = state_context;
   SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+  SilcClientEntry client = silc_packet_get_context(cmd->packet->stream);
+  SilcBuffer nidp, oidp = NULL;
+  SilcClientID new_id;
+  SilcUInt32 nick_len;
+  unsigned char *nick, *nickc;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
+  SILC_SERVER_COMMAND_CHECK(1, 1);
+
+  /* This command can come only from client */
+  if (!SILC_IS_CLIENT(client)) {
+    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
+                                    SILC_STATUS_ERR_OPERATION_ALLOWED, 0);
+    goto out;
+  }
+
+  /* Get nickname */
+  nick = silc_argument_get_arg_type(args, 1, &nick_len);
+  if (!nick) {
+    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+    goto out;
+  }
+
+  /* Truncate over long nicks */
+  if (nick_len > 128) {
+    nick_len = 128;
+    nick[nick_len - 1] = '\0';
+  }
+
+  /* Check for same nickname */
+  if (strlen(client->nickname) == nick_len &&
+      !memcmp(client->nickname, nick, nick_len)) {
+    nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);
+    goto send_reply;
+  }
+
+  /* Check for valid nickname string. */
+  nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL);
+  if (!nickc) {
+    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
+                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
+    goto out;
+  }
+
+  /* Create new Client ID */
+  if (!silc_server_create_client_id(thread->server, nickc, &new_id)) {
+    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
+                                    SILC_STATUS_ERR_OPERATION_ALLOWED, 0);
+    goto out;
+  }
+  silc_free(nickc);
+
+  oidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);
+
+  /* Replace the old nickname and ID with new ones.  This checks for
+     validity of the nickname too. */
+  if (!silc_server_replace_client_id(thread->server, &client->id, &new_id,
+                                    nick)) {
+    silc_server_command_status_reply(cmd, SILC_COMMAND_NICK,
+                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
+    goto out;
+  }
+
+  nidp = silc_id_payload_encode(&client->id, SILC_ID_CLIENT);
+
+#if 0
+  /* Send notify about nickname and ID change to network. */
+  silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+                                     SILC_BROADCAST(server), client->id,
+                                     &new_id, nick);
+
+  /* Send NICK_CHANGE notify to the client's channels */
+  silc_server_send_notify_on_channels(server, NULL, client,
+                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+                                     oidp->data, silc_buffer_len(oidp),
+                                     nidp->data, silc_buffer_len(nidp),
+                                     client->nickname,
+                                     strlen(client->nickname));
+#endif
+
+ send_reply:
+  /* Send the new Client ID as reply command back to client */
+  silc_server_send_command_reply(thread->server, cmd->packet->stream,
+                                SILC_COMMAND_NICK,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, nidp->data, silc_buffer_len(nidp),
+                                3, nick, nick_len);
+  silc_buffer_free(nidp);
+  silc_buffer_free(oidp);
+
+ out:
+  silc_server_command_free(cmd);
   return SILC_FSM_FINISH;
 }
 
@@ -589,10 +730,6 @@ SILC_FSM_STATE(silc_server_st_command_kill)
 
 /********************************** INFO ************************************/
 
-/* Server side of command INFO. This sends information about us to
-   the client.  If client requested specific server we will send the
-   command to that server. */
-
 SILC_FSM_STATE(silc_server_st_command_info)
 {
   return SILC_FSM_FINISH;
@@ -613,8 +750,6 @@ SILC_FSM_STATE(silc_server_st_command_stats)
 
 /********************************** PING ************************************/
 
-/* Server side of command PING. */
-
 SILC_FSM_STATE(silc_server_st_command_ping)
 {
   SilcServerThread thread = fsm_context;
@@ -622,36 +757,36 @@ SILC_FSM_STATE(silc_server_st_command_ping)
   SilcArgumentPayload args = silc_command_get_args(cmd->payload);
   SilcUInt32 tmp_len;
   unsigned char *tmp;
-  SilcServerID server_id;
+  SilcID id;
 
   SILC_SERVER_COMMAND_CHECK(1, 1);
 
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
   if (!tmp) {
-    silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload),
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
-                                         0);
+    silc_server_command_status_reply(cmd, silc_command_get(cmd->payload),
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
     goto out;
   }
-  if (!silc_id_payload_parse_id(tmp, tmp_len, NULL, &server_id,
-                               sizeof(server_id))) {
-    silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload),
-                                        SILC_STATUS_ERR_BAD_SERVER_ID, 0,
-                                        2, tmp, tmp_len);
+  if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) {
+    silc_server_command_status_data(cmd, silc_command_get(cmd->payload),
+                                   SILC_STATUS_ERR_BAD_SERVER_ID, 0,
+                                   2, tmp, tmp_len);
     goto out;
   }
 
-  if (SILC_ID_SERVER_COMPARE(&server_id, &thread->server->id)) {
-    /* Send our reply */
-    silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload),
-                                         SILC_STATUS_OK, 0);
-  } else {
-    silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload),
-                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
-                                        2, tmp, tmp_len);
+  /* Must be our ID */
+  if (!SILC_ID_SERVER_COMPARE(&id.u.server_id, &thread->server->id)) {
+    silc_server_command_status_data(cmd, silc_command_get(cmd->payload),
+                                   SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
+                                   2, tmp, tmp_len);
+    goto out;
   }
 
+  /* Send our reply */
+  silc_server_command_status_reply(cmd, silc_command_get(cmd->payload),
+                                  SILC_STATUS_OK, 0);
+
  out:
   silc_server_command_free(cmd);
   return SILC_FSM_FINISH;
index 89201f79baec9cb21601a608ea82500f8865413b..0bb03effe179b4bcc704a58c7e594888ecb5dbb2 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005, 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -23,6 +23,9 @@
 /***************************** State functions ******************************/
 
 SILC_FSM_STATE(silc_server_st_packet_command);
+SILC_FSM_STATE(silc_server_st_command_whois);
+SILC_FSM_STATE(silc_server_st_command_whowas);
+SILC_FSM_STATE(silc_server_st_command_identify);
 SILC_FSM_STATE(silc_server_st_command_nick);
 SILC_FSM_STATE(silc_server_st_command_list);
 SILC_FSM_STATE(silc_server_st_command_topic);
index 765e71261cab3d78c29408e4ae2598fde3a8ab8f..e8676ca829b696adb7077fe702812c8ee5d65e70 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
index 2737b902d38bf1e2a0a39b6f8964a65429bf5652..c940ba04a4455830ff05b01ca8b5f1980ddda8a7 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2002 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #include "silcserver.h"
 #include "server_internal.h"
 
-SILC_FSM_STATE(silc_server_st_command_whois)
+/************************** Types and definitions ***************************/
+
+/* Resolving entry */
+typedef struct SilcServerQueryResolveStruct {
+  struct SilcServerQueryResolveStruct *next;
+  SilcFSMThreadStruct thread;      /* FSM thread for waiting reply */
+  SilcServerPending pending;       /* Pending command context */
+  SilcPacketStream stream;         /* Resolving connection */
+  SilcID *ids;                     /* Resolved IDs */
+  unsigned int ids_count     : 30;  /* Number of resolved IDs */
+  unsigned int attached      :  1;  /* Set if attached to a resolving */
+  unsigned int local         :  1;  /* Set if client is local to us */
+} *SilcServerQueryResolve;
+
+/* Represents one error occurred during query */
+typedef struct {
+  SilcID id;                       /* ID */
+  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, normalized */
+  char *nick_server;               /* Queried nickname's server */
+  char *server_name;               /* Queried server name, normalized */
+  char *channel_name;              /* Queried channel name, normalized */
+  SilcID *ids;                     /* Queried IDs */
+  SilcUInt32 ids_count;                    /* number of queried IDs */
+  SilcUInt32 reply_count;          /* Requested reply count */
+  SilcDList attrs;                 /* Requested Attributes in WHOIS */
+  SilcFSMSemaStruct wait_resolve;   /* Resolving signaller */
+
+  /* Query session data */
+  SilcServerComman cmd;                    /* Command context for query */
+  SilcList clients;                /* Found clients */
+  SilcList servers;                /* Found servers */
+  SilcList channels;               /* Found channels */
+  SilcList resolve;                /* Clients to resolve */
+  SilcList resolvings;             /* Ongoing resolvings */
+  SilcServerQueryError errors;     /* Query errors */
+  SilcServerPending redirect;      /* Pending redirect */
+  SilcUInt16 errors_count;         /* number of errors */
+  SilcUInt8 resolve_retry;         /* Resolving retry count */
+  SilcCommand querycmd;                    /* Query command */
+} *SilcServerQuery;
+
+
+/************************ Static utility functions **************************/
+
+
+/********************************* WHOIS ************************************/
+
+SILC_FSM_STATE(silc_server_st_query_whois)
 {
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerCommand cmd = state_context;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+  SilcServerQuery query;
 
-  return SILC_FSM_FINISH;
+  SILC_LOG_DEBUG(("WHOIS query"));
+
+  query = silc_calloc(1, sizeof(*query));
+  if (!query) {
+    silc_server_command_free(cmd);
+    return SILC_FSM_FINISH;
+  }
+
+  query->querycmd = SILC_COMMAND_WHOIS;
+  query->cmd = cmd;
+
+  silc_fsm_set_state_context(fsm, query);
+
+  /* If we are normal server and query contains a nickname OR query
+     doesn't contain nickname or ids BUT does contain user attributes,
+     send it to the router */
+  if (server->server_type != SILC_ROUTER && !server->standalone &&
+      cmd->packet->stream != SILC_PRIMARY_ROUTE(server) &&
+      (silc_argument_get_arg_type(args, 1, NULL) ||
+       (!silc_argument_get_arg_type(args, 1, NULL) &&
+       !silc_argument_get_arg_type(args, 4, NULL) &&
+       silc_argument_get_arg_type(args, 3, NULL)))) {
+    /** Send query to router */
+    silc_fsm_next(fsm, silc_server_st_query_send_router);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Parse WHOIS query */
+  silc_fsm_next(fsm, silc_server_st_query_parse);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_FSM_STATE(silc_server_st_command_whowas)
+
+/********************************* WHOWAS ***********************************/
+
+SILC_FSM_STATE(silc_server_st_query_whowas)
 {
+  SilcServerThread thread = fsm_context;
+  SilcServerCommand cmd = state_context;
 
-  return SILC_FSM_FINISH;
+  SILC_LOG_DEBUG(("WHOWAS query"));
+
+  query = silc_calloc(1, sizeof(*query));
+  if (!query) {
+    silc_server_command_free(cmd);
+    return SILC_FSM_FINISH;
+  }
+
+  query->querycmd = SILC_COMMAND_WHOWAS;
+  query->cmd = cmd;
+
+  silc_fsm_set_state_context(fsm, query);
+
+  /* WHOWAS query is always sent to router if we are normal server */
+  if (server->server_type == SILC_SERVER && !server->standalone &&
+      cmd->packet->stream != SILC_PRIMARY_ROUTE(server)) {
+    /** Send query to router */
+    silc_fsm_next(fsm, silc_server_st_query_send_router);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Parse WHOWAS query */
+  silc_fsm_next(fsm, silc_server_st_query_parse);
+  return SILC_FSM_CONTINUE;
+}
+
+
+/******************************** IDENTIFY **********************************/
+
+SILC_FSM_STATE(silc_server_st_query_identify)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServerCommand cmd = state_context;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+
+  SILC_LOG_DEBUG(("IDENTIFY query"));
+
+  query = silc_calloc(1, sizeof(*query));
+  if (!query) {
+    silc_server_command_free(cmd);
+    return SILC_FSM_FINISH;
+  }
+
+  query->querycmd = SILC_COMMAND_IDENTIFY;
+  query->cmd = cmd;
+
+  silc_fsm_set_state_context(fsm, query);
+
+  /* If we are normal server and query does not contain IDs, send it directly
+     to router (it contains nickname, server name or channel name). */
+  if (server->server_type == SILC_SERVER && !server->standalone &&
+      cmd->packet->stream != SILC_PRIMARY_ROUTE(server) &&
+      !silc_argument_get_arg_type(args, 5, NULL)) {
+    /** Send query to router */
+    silc_fsm_next(fsm, silc_server_st_query_send_router);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Parse IDENTIFY query */
+  silc_fsm_next(fsm, silc_server_st_query_parse);
+  return SILC_FSM_CONTINUE;
+}
+
+
+/**************************** Query redirecting *****************************/
+
+/* Send the query to router for further processing */
+
+SILC_FSM_STATE(silc_server_st_query_send_router)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcBuffer tmpbuf;
+  SilcUInt16 cmd_ident, old_ident;
+
+  SILC_LOG_DEBUG(("Redirecting query to router"));
+
+  /* Send the command to our router */
+  cmd_ident = silc_server_cmd_ident(server);
+  old_ident = silc_command_get_ident(query->cmd->payload);
+  silc_command_set_ident(query->cmd->payload, cmd_ident);
+
+  tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
+  if (!tmpbuf || !silc_packet_send(SILC_PRIMARY_ROUTE(server),
+                                  SILC_PACKET_COMMAND, 0,
+                                  tmpbuf->data, silc_buffer_len(tmpbuf))) {
+    /** Error sending packet */
+    silc_server_query_send_error(server, query,
+                                SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
+    silc_fsm_next(fsm, silc_server_st_query_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_command_set_ident(query->cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+
+  /* Statistics */
+  server->stat.commands_sent++;
+
+  /* Continue parsing the query after receiving reply from router */
+  query->redirect = silc_server_command_pending(thread, query->redirect_ident);
+  if (!query->redirect) {
+    /** No memory */
+    silc_server_query_send_error(server, query,
+                                SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
+    silc_fsm_next(fsm, silc_server_st_query_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Wait router reply */
+  query->resolved = TRUE;
+  silc_fsm_next(fsm, silc_server_st_query_router_reply)
+  return SILC_FSM_CONTINUE;
+}
+
+/* Wait for router reply and process the reply when it arrives. */
+
+SILC_FSM_STATE(silc_server_st_query_router_reply)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcServerPending pending = query->redirect;
+  SilcBool timedout;
+
+  /* Wait here for the reply */
+  SILC_FSM_SEMA_TIMEDWAIT(&pending->wait_reply, 10, 0, &timedout);
+
+  if (timedout) {
+    /** Timeout waiting reply */
+    silc_server_command_pending_free(thread, pending);
+    silc_server_query_send_error(server, query, SILC_STATUS_ERR_TIMEDOUT, 0);
+    silc_fsm_next(fsm, silc_server_st_query_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Check if the query failed */
+  if (!silc_command_get_status(pending->reply->payload, NULL, NULL)) {
+    SilcBuffer buffer;
+
+    SILC_LOG_DEBUG(("Sending error to original query"));
+
+    /* Send the same command reply payload which contains the error */
+    silc_command_set_command(pending->reply->payload, query->querycmd);
+    silc_command_set_ident(pending->reply->payload,
+                          silc_command_get_ident(query->cmd->payload));
+    buffer = silc_command_payload_encode_payload(pending->reply->payload);
+    if (buffer)
+      silc_packet_send(query->cmd->packet->stream,
+                      SILC_PACKET_COMMAND_REPLY, 0,
+                      buffer->data, silc_buffer_len(buffer));
+    silc_buffer_free(buffer);
+
+    /* Statistics */
+    server->stat.commands_sent++;
+
+    /** Query error received */
+    silc_server_command_pending_free(thread, pending);
+    silc_fsm_next(fsm, silc_server_st_query_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_server_command_pending_free(thread, pending);
+
+  /** Parse query command */
+  silc_fsm_next(fsm, silc_server_st_query_parse);
+  return SILC_FSM_CONTINUE;
+}
+
+/***************************** Query processing *****************************/
+
+/* Parse the command query */
+
+SILC_FSM_STATE(silc_server_st_query_parse)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServerQuery query = state_context;
+  SilcServerCommand cmd = query->cmd;
+  SilcArgumentPayload args = silc_command_get_args(cmd->payload);
+  SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(args);
+  unsigned char *tmp;
+  SilcID id;
+  int i;
+
+  SILC_LOG_DEBUG(("Parsing %s query",
+                 silc_get_command_name(query->querycmd)));
+
+  switch (query->querycmd) {
+
+  case SILC_COMMAND_WHOIS:
+    /* Get requested attributes if set */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN)
+      query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
+
+    /* Get Client IDs if present. Take IDs always instead of nickname. */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (!tmp) {
+      /* No IDs present */
+
+      /* Get nickname */
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      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);
+
+       /** Not enough arguments */
+       silc_fsm_next(fsm, silc_server_st_query_error);
+       return SILC_FSM_CONTINUE;
+      }
+
+      /* Get the nickname@server string and parse it */
+      if (tmp && ((tmp_len > 128) ||
+                 !silc_parse_userfqdn(tmp, &query->nickname,
+                                      &query->nick_server))) {
+       /** Bad nickname */
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
+       silc_fsm_next(fsm, silc_server_st_query_error);
+       return SILC_FSM_CONTINUE;
+      }
+
+      /* Check nickname */
+      if (tmp) {
+       tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                                   SILC_STRING_UTF8, 128, &tmp_len);
+       if (!tmp) {
+         /** Bad nickname */
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
+         silc_fsm_next(fsm, silc_server_st_query_error);
+         return SILC_FSM_CONTINUE;
+       }
+       /* XXX why free nickname */
+       silc_free(query->nickname);
+       query->nickname = tmp;
+      }
+
+    } else {
+      /* Parse the IDs included in the query */
+      query->ids = silc_calloc(argc - 3, sizeof(*query->ids));
+      if (!query->ids) {
+       /** No memory */
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
+       silc_fsm_next(fsm, silc_server_st_query_error);
+       return SILC_FSM_CONTINUE;
+      }
+
+      for (i = 0; i < argc - 3; i++) {
+       tmp = silc_argument_get_arg_type(args, i + 4, &tmp_len);
+       if (!tmp)
+         continue;
+
+       if (!silc_id_payload_parse_id(tmp, tmp_len, &id) ||
+           id.type != SILC_ID_CLIENT) {
+         silc_server_query_add_error(server, query, 1, i + 4,
+                                     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 &&
+           !silc_server_find_client_by_id(server, &client_id, TRUE, NULL)) {
+         /** Send query to router */
+         silc_free(query->ids);
+         query->ids = NULL;
+         query->ids_count = 0;
+         silc_fsm_next(fsm, silc_server_st_query_send_router);
+         return SILC_FSM_CONTINUE;
+       }
+
+       query->ids[query->ids_count] = id;
+       query->ids_count++;
+      }
+    }
+
+    /* Get the max count of reply messages allowed */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp && tmp_len == sizeof(SilcUInt32))
+      SILC_GET32_MSB(query->reply_count, tmp);
+    break
+
+  case SILC_COMMAND_WHOWAS:
+    /* Get nickname */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp) {
+      /** Not enough arguments */
+      silc_server_query_send_error(server, query,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+      silc_fsm_next(fsm, silc_server_st_query_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    /* Get the nickname@server string and parse it */
+    if (tmp_len > 128 ||
+       !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
+      /** Bad nickname */
+      silc_server_query_send_error(server, query,
+                                  SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      silc_fsm_next(fsm, silc_server_st_query_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    /* Check nickname */
+    tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
+                               SILC_STRING_UTF8, 128, &tmp_len);
+    if (!tmp) {
+      /** Bad nickname */
+      silc_server_query_send_error(server, query,
+                                  SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      silc_fsm_next(fsm, silc_server_st_query_error);
+      return SILC_FSM_CONTINUE;
+    }
+    /* XXX why free nickname */
+    silc_free(query->nickname);
+    query->nickname = tmp;
+
+    /* Get the max count of reply messages allowed */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp && tmp_len == sizeof(SilcUInt32))
+      SILC_GET32_MSB(query->reply_count, tmp);
+    break;
+
+  case SILC_COMMAND_IDENTIFY:
+    /* Get IDs if present. Take IDs always instead of names. */
+    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+    if (!tmp) {
+      /* No IDs present */
+
+      /* Try get nickname */
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (tmp) {
+       /* 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, 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) {
+         /** Bad nickname */
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
+         silc_fsm_next(fsm, silc_server_st_query_error);
+         return SILC_FSM_CONTINUE;
+       }
+       /* XXX why free nickname */
+       silc_free(query->nickname);
+       query->nickname = tmp;
+      }
+
+      /* Try get server name */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (tmp) {
+       /* Check server name */
+       tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
+                                   256, &tmp_len);
+       if (!tmp) {
+         /** Bad server name */
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_SERVER, 0);
+         silc_fsm_next(fsm, silc_server_st_query_error);
+         return SILC_FSM_CONTINUE;
+       }
+       query->server_name = tmp;
+      }
+
+      /* Get channel name */
+      tmp = silc_argument_get_arg_type(args, 3, &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) {
+         /** Bad channel name */
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_BAD_CHANNEL, 0);
+         silc_fsm_next(fsm, silc_server_st_query_error);
+         return SILC_FSM_CONTINUE;
+       }
+       query->channel_name = tmp;
+      }
+
+      if (!query->nickname && !query->server_name && !query->channel_name) {
+       /** Nothing was queried */
+       silc_server_query_send_error(server, query,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       silc_fsm_next(fsm, silc_server_st_query_error);
+       return SILC_FSM_CONTINUE;
+      }
+
+    } else {
+      /* Parse the IDs included in the query */
+      query->ids = silc_calloc(argc - 4, sizeof(*query->ids));
+
+      for (i = 0; i < argc - 4; i++) {
+       tmp = silc_argument_get_arg_type(args, i + 5, &tmp_len);
+       if (!tmp)
+         continue;
+
+       if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) {
+         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 (id.type == SILC_ID_CLIENT) {
+           if (!silc_server_find_client_by_id(server, id, TRUE, NULL)) {
+             /** Send query to router */
+             silc_free(query->ids);
+             query->ids = NULL;
+             query->ids_count = 0;
+             silc_fsm_next(fsm, silc_server_st_query_send_router);
+             return SILC_FSM_CONTINUE;
+           }
+         } else {
+           /* For now all other ID's except Client ID's are explicitly
+              sent to router for resolving. */
+
+           /** Send query to router */
+           silc_free(query->ids);
+           query->ids = NULL;
+           query->ids_count = 0;
+           silc_fsm_next(fsm, silc_server_st_query_send_router);
+           return SILC_FSM_CONTINUE;
+         }
+       }
+
+       query->ids[query->ids_count] = id;
+       query->ids_count++;
+      }
+    }
+
+    /* Get the max count of reply messages allowed */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp && tmp_len == sizeof(SilcUInt32))
+      SILC_GET32_MSB(query->reply_count, tmp);
+    break;
+  }
+
+  /** Find entries for query */
+  silc_fsm_next(fsm, silc_server_st_query_find);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Find the entries according to the query */
+
+SILC_FSM_STATE(silc_server_st_query_find)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcServerCommand cmd = query->cmd;
+  SilcIDCacheEntry id_entry;
+  SilcID *id;
+  void *entry;
+  int i;
+
+  SILC_LOG_DEBUG(("Finding entries with %s query",
+                 silc_get_command_name(query->querycmd)));
+
+  if (query->nickname) {
+    /* Find by nickname */
+    if (!silc_server_find_clients(server, query->nickname, &query->clients))
+      silc_server_query_add_error(server, query, 1, 1,
+                                 SILC_STATUS_ERR_NO_SUCH_NICK);
+  }
+
+  if (query->server_name) {
+    /* Find server by name */
+    if (!silc_server_find_server_by_name(server, query->server_name, TRUE,
+                                        &id_entry))
+      silc_server_query_add_error(server, query, 1, 2,
+                                 SILC_STATUS_ERR_NO_SUCH_SERVER);
+    else
+      silc_list_add(query->servers, id_entry);
+  }
+
+  if (query->channel_name) {
+    /* Find channel by name */
+    if (!silc_server_find_channel_by_name(server, query->channel_name,
+                                         &id_entry))
+      silc_server_query_add_error(server, query, 1, 3,
+                                 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+    else
+      silc_list_add(query->channels, id_entry);
+  }
+
+  if (query->ids_count) {
+    /* Find entries by the queried IDs */
+    for (i = 0; i < query->ids_count; i++) {
+      id = &query->ids[i];
+
+      switch (id->type) {
+
+      case SILC_ID_CLIENT:
+       /* Get client entry */
+       if (!silc_server_find_client_by_id(server, &id->u.client_id, TRUE,
+                                          &id_entry)) {
+         silc_server_query_add_error(server, query, 0, i,
+                                     SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+         continue;
+       }
+
+       silc_list_add(query->clients, id_entry);
+       break;
+
+      case SILC_ID_SERVER:
+       /* Get server entry */
+       if (!silc_server_find_server_by_id(server, &id->u.server_id, TRUE,
+                                          &id_entry)) {
+         silc_server_query_add_error(server, query, 0, i,
+                                     SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
+         continue;
+       }
+
+       silc_list_add(query->servers, id_entry);
+       break;
+
+      case SILC_ID_CHANNEL:
+       /* Get channel entry */
+       if (!silc_server_find_channel_by_id(server, &id->u.channel_id,
+                                           &id_entry)) {
+         silc_server_query_add_error(server, query, 0, i,
+                                     SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
+         continue;
+       }
+
+       silc_list_add(query->channels, id_entry);
+       break;
+
+      default:
+       break;
+      }
+    }
+  }
+
+  /* Check the attributes to narrow down the search by using them. */
+  if (query->attrs) {
+    /** Check user attributes */
+    silc_fsm_next(fsm, silc_server_st_query_check_attrs);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Process found entries */
+  silc_fsm_next(fsm, silc_server_st_query_process);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Check user attributes to narrow down clients in WHOIS query */
+
+SILC_FSM_STATE(silc_server_st_query_check_attrs)
+{
+
+  /** Proecss found entries */
+  silc_fsm_next(fsm, silc_server_st_query_process);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Process found entries */
+
+SILC_FSM_STATE(silc_server_st_query_process)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcServerCommand cmd = query->cmd;
+  SilcServerQueryResolve res;
+  SilcIDCacheEntry id_entry;
+  SilcClientEntry client_entry;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel_entry;
+  SilcID *id;
+  void *entry;
+  int i;
+
+  SILC_LOG_DEBUG(("Process %s query",
+                 silc_get_command_name(query->querycmd)));
+
+  SILC_LOG_DEBUG(("Querying %d clients", silc_list_count(query->clients)));
+  SILC_LOG_DEBUG(("Querying %d servers", silc_list_count(query->servers)));
+  SILC_LOG_DEBUG(("Querying %d channels", silc_list_count(query->channels)));
+
+  /* If nothing was found, then just send the errors */
+  if (!silc_list_count(query->clients) &&
+      !silc_list_count(query->channels) &&
+      !silc_list_count(query->servers)) {
+    /** Nothing found, send errors */
+    silc_fsm_next(fsm, silc_server_st_query_reply);
+    return SILC_FSM_CONTINUE;
+  }
+
+#if 0
+  /* If caller does not want us to resolve anything (has resolved already)
+     then just continue with sending the reply */
+  if (!resolve) {
+    silc_server_query_send_reply(server, query, clients, clients_count,
+                                servers, servers_count, channels,
+                                channels_count);
+    silc_free(clients);
+    silc_free(servers);
+    silc_free(channels);
+    return;
+  }
+#endif
+
+  /* Now process all found information and if necessary do some more
+     resolving. */
+  switch (query->querycmd) {
+
+  case SILC_COMMAND_WHOIS:
+    silc_list_start(query->clients);
+    while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
+      client_entry = id_entry->context;
+
+      /* Ignore unregistered clients */
+      if (!SILC_IS_REGISTERED(client_entry)) {
+       silc_list_del(query->clients, id_entry);
+       continue;
+      }
+
+      /* If Requested Attributes is set then we always resolve the client
+        information, if not then check whether the entry is complete or not
+        and decide whether we need to resolve the missing information. */
+      if (!query->attrs) {
+
+       /* Even if nickname and stuff are present, we may need to resolve
+          the entry on normal server. */
+       if (client_entry->nickname && client_entry->username &&
+           client_entry->userinfo) {
+
+         /* If we are router, client is local to us, or client is on channel
+            we do not need to resolve the client information. */
+         if (server->server_type != SILC_SERVER ||
+             SILC_IS_LOCAL(client_entry)||
+             silc_hash_table_count(client_entry->channels) ||
+             query->resolved)
+           continue;
+       }
+      }
+
+      /* Remove the NOATTR status periodically */
+      if (client_entry->data.noattr &&
+         client_entry->updated + 600 < time(NULL))
+       client_entry->data.noattr = FALSE;
+
+      /* 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. */
+      if (query->attrs && SILC_IS_LOCAL(client_entry) &&
+         (client_entry->mode & SILC_UMODE_DETACHED ||
+          client_entry->data.noattr))
+       continue;
+
+#if 0
+      /* 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;
+#endif
+
+      /* Mark this entry to be resolved */
+      silc_list_add(query->resolve, id_entry);
+    }
+    break;
+
+  case SILC_COMMAND_WHOWAS:
+    silc_list_start(query->clients);
+    while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
+      client_entry = id_entry->context;
+
+      /* Take only unregistered clients */
+      if (SILC_IS_REGISTERED(client_entry)) {
+       silc_list_del(query->clients, id_entry);
+       continue;
+      }
+
+      /* If both nickname and username are present no resolving is needed */
+      if (client_entry->nickname && client_entry->username)
+       continue;
+
+      /* Mark this entry to be resolved */
+      silc_list_add(query->resolve, id_entry);
+    }
+    break;
+
+  case SILC_COMMAND_IDENTIFY:
+    silc_list_start(query->clients);
+    while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
+      client_entry = id_entry->context;
+
+      /* Ignore unregistered clients */
+      if (!SILC_IS_REGISTERED(client_entry))
+       continue;
+
+      /* Even if nickname is present, we may need to resolve the entry
+        on normal server. */
+      if (client_entry->nickname) {
+
+       /* If we are router, client is local to us, or client is on channel
+          we do not need to resolve the client information. */
+       if (server->server_type != SILC_SERVER ||
+           SILC_IS_LOCAL(client_entry)||
+           silc_hash_table_count(client_entry->channels) ||
+           query->resolved)
+         continue;
+      }
+
+      /* Mark this entry to be resolved */
+      silc_list_add(query->resolve, id_entry);
+    }
+    break;
+  }
+
+  /* If we need to resolve entries, do it now */
+  if (silc_list_count(query->resolve)) {
+    /** Resolve entries */
+    silc_fsm_next(fsm, silc_server_st_query_resolve);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Send reply to query */
+  silc_fsm_next(fsm, silc_server_st_query_reply);
+  return SILC_FSM_CONTINUE;
 }
 
-SILC_FSM_STATE(silc_server_st_command_identify)
+/* Resolve incomplete client entries.  Other types of entries need not
+   resolving. */
+
+SILC_FSM_STATE(silc_server_st_query_resolve)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcArgumentPayload cmd_args = silc_command_get_args(query->cmd->payload);
+  SilcServerQueryResolve res;
+  SilcIDCacheEntry id_entry;
+  unsigned char args[256][28];
+  SilcUInt32 arg_lens[256], arg_types[256], argc = 0;
+  SilcBuffer res_cmd;
+  int i;
+
+  SILC_LOG_DEBUG(("Resolve incomplete entries"));
+
+  silc_list_start(query->resolve);
+  while ((id_entry = silc_list_get(query->resolve)) != SILC_LIST_END) {
+    client_entry = id_entry->context;
+
+    /* If entry is being resolved, attach to that resolving */
+    if (client_entry->data.resolving) {
+      res = silc_calloc(1, sizeof(*res));
+      if (!res)
+       continue;
+
+      silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE);
+      res->stream = client_entry->stream;
+
+      res->pending =
+       silc_server_command_pending(thread, client_entry->resolve_cmd_ident);
+      if (!res->pending) {
+       SILC_LOG_ERROR(("BUG: No pending command for resolving client entry"));
+       continue;
+      }
+
+      res->attached = TRUE;
+      silc_list_add(query->resolvings, res);
+      continue;
+    }
+
+    /* Check if we have resolving destination already set */
+    silc_list_start(query->resolvings);
+    while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END)
+      if (res->stream == client_entry->stream && !res->attached)
+       break;
+
+    if (!res) {
+      /* Create new resolving context */
+      res = silc_calloc(1, sizeof(*res));
+      if (!res)
+       continue;
+
+      silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE);
+      res->stream = client_entry->stream;
+
+      res->pending =
+       silc_server_command_pending(thread, silc_server_cmd_ident(server));
+      if (!res->pending)
+       continue;
+
+      silc_list_add(query->resolvings, res);
+    }
+
+    /* Mark the entry as being resolved */
+    client_entry->data.resolving = TRUE;
+    client_entry->data.resolved = FALSE;
+    client_entry->resolve_cmd_ident = res->pending->cmd_ident;
+    client_entry->updated = time(NULL);
+
+    if (SILC_IS_LOCAL(client_entry))
+      res->local = TRUE;
+
+    switch (query->querycmd) {
+    case SILC_COMMAND_WHOIS:
+    case SILC_COMMAND_IDENTIFY:
+      res->ids = silc_realloc(res->ids, sizeof(*res->ids) *
+                             (res->ids_count + 1));
+      if (!res->ids)
+       continue;
+
+      res->ids[res->ids_count++].u.client_id = client_entry->id;
+      break;
+
+    case SILC_COMMAND_WHOWAS:
+      break;
+    }
+  }
+
+  SILC_LOG_DEBUG(("Sending the resolvings"));
+
+  /* Send the resolvings */
+  silc_list_start(query->resolvings);
+  while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END) {
+
+    if (!res->attached) {
+
+      switch (query->querycmd) {
+      case SILC_COMMAND_WHOIS:
+      case SILC_COMMAND_IDENTIFY:
+
+       /* If Requested Attributes were present put them to this resolving */
+       if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
+         arg_types[argc] = 3;
+         args[argc] = silc_argument_get_arg_type(cmd_args, 3,
+                                                 &arg_lens[argc]);
+         argc++;
+       }
+
+       /* Encode IDs */
+       for (i = 0; i < res->ids_count; i++) {
+         arg_types[argc] = (query->querycmd == SILC_COMMAND_WHOIS ?
+                            4 + i : 5 + i);
+         silc_id_id2str(&res->ids[argc].u.client_id, SILC_ID_CLIENT,
+                        args[argc], sizeof(args[argc]), &arg_lens[argc]);
+         argc++;
+         if (i + 1 > 255)
+           break;
+       }
+
+       /* Send the command */
+       res_cmd = silc_command_payload_encode(query->querycmd, argc,
+                                             args, arg_lens, arg_types,
+                                             res->pending->cmd_ident);
+       if (!res_cmd) {
+         /** No memory */
+         silc_server_query_send_error(server, query,
+                                      SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
+         silc_fsm_next(fsm, silc_server_st_query_error);
+         return SILC_FSM_CONTINUE;
+       }
+
+       silc_packet_send(res->stream, SILC_PACKET_COMMAND, 0,
+                        res_cmd->data, silc_buffer_send(res_cmd));
+       silc_buffer_free(res_cmd);
+       silc_free(res->ids);
+       res->ids = NULL;
+
+       /* Statistics */
+       server->stat.commands_sent++;
+       break;
+
+      case SILC_COMMAND_WHOWAS:
+       /* Send WHOWAS command */
+       silc_server_send_command(server, res->stream, query->querycmd,
+                                res->pending->cmd_ident, 1,
+                                1, query->nickname, strlen(query->nickname));
+       break;
+      }
+    }
+
+    /*** Resolve */
+    silc_fsm_set_state_context(&res->thread, query);
+    silc_fsm_start_sync(&res->thread, silc_server_st_query_wait_resolve);
+  }
+
+  /** Wait all resolvings */
+  silc_fsm_next(fsm, silc_server_st_query_resolved);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Wait for resolving command reply */
+
+SILC_FSM_STATE(silc_server_st_query_wait_resolve)
 {
+  SilcServerQueryResolve res = fsm_context;
+  SilcServerQuery query = state_context;
+  SilcBool timedout;
+
+  /* Wait here for the reply */
+  SILC_FSM_SEMA_TIMEDWAIT(&res->pending->wait_reply,
+                         res->local ? 3 : 10, 0, &timedout);
+
+
+
+  silc_list_del(query->resolvings, res);
+  silc_server_command_pending_free(res->pending);
+  silc_free(res);
+
+  /* Signal main thread that reply was received */
+  SILC_FSM_SEMA_POST(&query->wait_resolve);
+
   return SILC_FSM_FINISH;
 }
+
+/* Wait here that all resolvings has been received */
+
+SILC_FSM_STATE(silc_server_st_query_resolved)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcServerCommand cmd = query->cmd;
+
+  /* Wait here until all resolvings has arrived */
+  SILC_FSM_SEMA_WAIT(&query->wait_resolve);
+  if (silc_list_count(query->resolvings) > 0)
+    return SILC_FSM_CONTINUE;
+
+}
+
+/* Send the reply to the query. */
+
+SILC_FSM_STATE(silc_server_st_query_reply)
+{
+  SilcServerThread thread = fsm_context;
+  SilcServer server = thread->server;
+  SilcServerQuery query = state_context;
+  SilcServerCommand cmd = query->cmd;
+  SilcIDCacheEntry id_entry;
+
+}
index 4ce698efc468b10410216500aa7b350ccdfc4975..6b5b38ebbfb7441dd2f0a37d4d718d241deac72f 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005 Pekka Riikonen
+  Copyright (C) 1997 - 2006 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #include "silcserver.h"
 #include "server_internal.h"
 
+/* Return next available command identifier. */
+
+SilcUInt16 silc_server_cmd_ident(SilcServer server)
+{
+  SilcUInt16 cmd_ident;
+
+  silc_mutex_lock(server->lock);
+  cmd_ident = ++server->cmd_ident;
+  silc_mutex_unlock(server->lock);
+
+  return cmd_ident;
+}
+
+/* XXX locking */
+
 SilcBool silc_server_check_watcher_list(SilcServer server,
                                    SilcClientEntry client,
                                    const char *new_nick,
@@ -125,6 +140,7 @@ SilcBool silc_server_create_client_id(SilcServer server, char *nickname,
   silc_hash_make(server->md5hash, nickname, strlen(nickname), hash);
 
   /* Create the ID */
+  memset(new_id, 0, sizeof(*new_id));
   memcpy(new_id->ip.data, server->id.ip.data, server->id.ip.data_len);
   new_id->ip.data_len = server->id.ip.data_len;
   new_id->rnd = silc_rng_get_byte(server->rng);