Preparations for Requested Attributes support.
[silc.git] / apps / silcd / server_query.c
1 /*
2
3   server_query.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 /* XXX TODO Requested Attributes to WHOIS */
22
23 #include "serverincludes.h"
24 #include "server_internal.h"
25
26 typedef struct {
27   SilcSocketConnection sock;        /* Connection of this query */
28   unsigned char **arg;              /* Query argument */
29   SilcUInt32 *arg_lens;             /* Query argument lengths */
30   SilcUInt32 *arg_types;            /* Query argument types */
31   SilcUInt32 argc;                  /* Number of query arguments */
32   SilcUInt32 timeout;               /* Max timeout for query to complete */
33   SilcUInt16 ident;                 /* Query command identifier */
34 } *SilcServerQueryList;
35
36 /* Represents an SILC ID */
37 typedef struct {
38   void *id;                         /* ID */
39   SilcIdType id_type;               /* ID type */
40   SilcUInt16 ident;                 /* Command identifier */
41 } *SilcServerQueryID;
42
43 /* Represents one error occurred during query */
44 typedef struct {
45   void *id;                         /* ID */
46   SilcIdType id_type;               /* ID type */
47   SilcUInt16 index;                 /* Index to IDs */
48   unsigned int from_cmd : 1;        /* TRUE if `index' is from command args,
49                                        otherwise from query->ids */
50   unsigned int error : 7;           /* The actual error (SilcStatus) */
51 } *SilcServerQueryError;
52
53 /* Query session context */
54 typedef struct {
55   /* Queried data */
56   char *nickname;                   /* Queried nickname */
57   char *nick_server;                /* Queried nickname's server */
58   char *server_name;                /* Queried server name */
59   char *channel_name;               /* Queried channel name */
60   SilcServerQueryID ids;            /* Queried IDs */
61   SilcUInt32 ids_count;             /* number of queried IDs */
62   SilcUInt32 reply_count;           /* Requested reply count */
63   SilcDList attrs;                  /* Requested Attributes in WHOIS */
64
65   /* Query session data */
66   SilcServerCommandContext cmd;     /* Command context for query */
67   SilcServerQueryList querylist;    /* Temporary query list context */
68   SilcServerQueryID queries;        /* Ongoing queries */
69   SilcServerQueryError errors;      /* Query errors */
70   SilcUInt16 querylist_count;       /* Number of query lists */
71   SilcUInt16 queries_count;         /* Number of ongoing queries */
72   SilcUInt16 queries_left;          /* Number of ongoing queries left */
73   SilcUInt16 errors_count;          /* number of errors */
74   unsigned int querycmd : 7;        /* Query command (SilcCommand) */
75   unsigned int resolved : 1;        /* TRUE if normal server has resolved
76                                        information from router */
77 } *SilcServerQuery;
78
79 void silc_server_query_free(SilcServerQuery query);
80 void silc_server_query_send_error(SilcServer server,
81                                   SilcServerQuery query,
82                                   SilcStatus error, ...);
83 void silc_server_query_add_error(SilcServer server,
84                                  SilcServerQuery query,
85                                  bool from_cmd,
86                                  SilcUInt32 index,
87                                  SilcStatus error);
88 void silc_server_query_add_error_id(SilcServer server,
89                                     SilcServerQuery query,
90                                     SilcStatus error,
91                                     void *id, SilcIdType id_type);
92 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
93 void silc_server_query_send_router_reply(void *context, void *reply);
94 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
95 void silc_server_query_process(SilcServer server, SilcServerQuery query,
96                                bool resolve);
97 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
98                                SilcSocketConnection sock,
99                                SilcClientEntry client_entry);
100 void silc_server_query_resolve_reply(void *context, void *reply);
101 void silc_server_query_send_reply(SilcServer server,
102                                   SilcServerQuery query,
103                                   SilcClientEntry *clients,
104                                   SilcUInt32 clients_count,
105                                   SilcServerEntry *servers,
106                                   SilcUInt32 servers_count,
107                                   SilcChannelEntry *channels,
108                                   SilcUInt32 channels_count);
109 unsigned char *silc_server_query_reply_attrs(SilcServer server,
110                                              SilcServerQuery query,
111                                              SilcUInt32 *attrs_len);
112
113 /* Free the query context structure and all allocated resources. */
114
115 void silc_server_query_free(SilcServerQuery query)
116 {
117   int i;
118
119   silc_server_command_free(query->cmd);
120
121   for (i = 0; i < query->queries_count; i++)
122     silc_free(query->queries[i].id);
123   silc_free(query->queries);
124
125   silc_free(query->nickname);
126   silc_free(query->nick_server);
127   silc_free(query->server_name);
128   silc_free(query->channel_name);
129
130   for (i = 0; i < query->ids_count; i++)
131     silc_free(query->ids[i].id);
132   silc_free(query->ids);
133
134   if (query->attrs)
135     silc_attribute_payload_list_free(query->attrs);
136
137   for (i = 0; i < query->errors_count; i++)
138     silc_free(query->errors[i].id);
139   silc_free(query->errors);
140
141   memset(query, 'F', sizeof(*query));
142   silc_free(query);
143 }
144
145 /* Send error reply indicated by the `error' to the original sender of
146    the query. */
147
148 void silc_server_query_send_error(SilcServer server,
149                                   SilcServerQuery query,
150                                   SilcStatus error, ...)
151 {
152   va_list va;
153   unsigned char *data = NULL;
154   SilcUInt32 data_len = 0, data_type = 0, argc = 0;
155
156   va_start(va, error);
157   data_type = va_arg(va, SilcUInt32);
158   if (data_type) {
159     argc = 1;
160     data = va_arg(va, unsigned char *);
161     data_len = va_arg(va, SilcUInt32);
162   }
163
164   SILC_LOG_DEBUG(("ERROR: %s (%d)", silc_get_status_message(error), error));
165
166   /* Send the command reply with error */
167   silc_server_send_command_reply(server, query->cmd->sock,
168                                  query->querycmd, error, 0, 
169                                  silc_command_get_ident(query->cmd->payload),
170                                  argc, data_type, data, data_len);
171   va_end(va);
172 }
173
174 /* Add error to error list.  Multiple errors may occur during the query
175    processing and this function can be used to add one error.  The
176    `index' is the index to the command context which includes the argument
177    which caused the error, or it is the index to query->ids, depending
178    on value of `from_cmd'. */
179
180 void silc_server_query_add_error(SilcServer server,
181                                  SilcServerQuery query,
182                                  bool from_cmd,
183                                  SilcUInt32 index,
184                                  SilcStatus error)
185 {
186   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
187                                (query->errors_count + 1));
188   if (!query->errors)
189     return;
190   query->errors[query->errors_count].index = index;
191   query->errors[query->errors_count].from_cmd = from_cmd;
192   query->errors[query->errors_count].error = error;
193   query->errors[query->errors_count].id = NULL;
194   query->errors[query->errors_count].id_type = 0;
195   query->errors_count++;
196 }
197
198 /* Same as silc_server_query_add_error but adds the ID data to be used
199    with error sending with this error type. */
200
201 void silc_server_query_add_error_id(SilcServer server,
202                                     SilcServerQuery query,
203                                     SilcStatus error,
204                                     void *id, SilcIdType id_type)
205 {
206   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
207                                (query->errors_count + 1));
208   if (!query->errors)
209     return;
210   query->errors[query->errors_count].index = 0;
211   query->errors[query->errors_count].from_cmd = FALSE;
212   query->errors[query->errors_count].error = error;
213   query->errors[query->errors_count].id = silc_id_dup(id, id_type);
214   query->errors[query->errors_count].id_type = id_type;
215   query->errors_count++;
216 }
217
218 /* Processes query as command.  The `query' is the command that is
219    being processed indicated by the `cmd'.  The `query' can be one of
220    the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
221    SILC_COMMAND_IDENTIFY.  This function handles the reply sending
222    to the entity who sent this query to us automatically.  Returns
223    TRUE if the query is being processed or FALSE on error. */
224
225 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
226                                SilcServerCommandContext cmd)
227 {
228   SilcServerQuery query;
229
230   SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
231
232   query = silc_calloc(1, sizeof(*query));
233   query->querycmd = querycmd;
234   query->cmd = silc_server_command_dup(cmd);
235
236   switch (querycmd) {
237
238   case SILC_COMMAND_WHOIS:
239     /* If we are normal server and query contains nickname, send it
240        directly to router. */
241     if (server->server_type == SILC_SERVER && !server->standalone &&
242         cmd->sock != SILC_PRIMARY_ROUTE(server) &&
243         silc_argument_get_arg_type(cmd->args, 1, NULL)) {
244       silc_server_query_send_router(server, query);
245       return TRUE;
246     }
247     break;
248
249   case SILC_COMMAND_WHOWAS:
250     /* WHOWAS query is always sent to router if we are normal server */
251     if (server->server_type == SILC_SERVER && !server->standalone &&
252         cmd->sock != SILC_PRIMARY_ROUTE(server)) {
253       silc_server_query_send_router(server, query);
254       return TRUE;
255     }
256     break;
257
258   case SILC_COMMAND_IDENTIFY:
259     /* If we are normal server and query does not contain IDs, send it
260        directly to router (it contains nickname, server name or channel
261        name). */
262     if (server->server_type == SILC_SERVER && !server->standalone &&
263         cmd->sock != SILC_PRIMARY_ROUTE(server) &&
264         !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
265       silc_server_query_send_router(server, query);
266       return TRUE;
267     }
268     break;
269
270   default:
271     SILC_LOG_ERROR(("Bad query using %d command", querycmd));
272     silc_server_query_free(query);
273     return FALSE;
274   }
275
276   /* Now parse the request */
277   silc_server_query_parse(server, query);
278
279   return TRUE;
280 }
281
282 /* Send the received query to our primary router since we could not
283    handle the query directly.  We will reprocess the query after our
284    router replies back. */
285
286 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
287 {
288   SilcBuffer tmpbuf;
289   SilcUInt16 old_ident;
290
291   SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
292
293   /* Send WHOIS command to our router */
294   old_ident = silc_command_get_ident(query->cmd->payload);
295   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
296   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
297   silc_server_packet_send(server, 
298                           SILC_PRIMARY_ROUTE(server),
299                           SILC_PACKET_COMMAND, 0,
300                           tmpbuf->data, tmpbuf->len, TRUE);
301   silc_command_set_ident(query->cmd->payload, old_ident);
302   silc_buffer_free(tmpbuf);
303
304   query->resolved = TRUE;
305
306   /* Continue parsing the query after received reply from router */
307   silc_server_command_pending(server, query->querycmd, server->cmd_ident,
308                               silc_server_query_send_router_reply, query);
309 }
310
311 /* Reply callback called after primary router has replied to our initial
312    sending of the query to it.  We will proceed the query in this function. */
313
314 void silc_server_query_send_router_reply(void *context, void *reply)
315 {
316   SilcServerQuery query = context;
317   SilcServer server = query->cmd->server;
318   SilcServerCommandReplyContext cmdr = reply;
319
320   SILC_LOG_DEBUG(("Received reply from router to query"));
321
322   /* Check if router sent error reply */
323   if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
324     SilcBuffer buffer;
325
326     SILC_LOG_DEBUG(("Sending error to original query"));
327
328     /* Send the same command reply payload which contains the error */
329     silc_command_set_command(cmdr->payload, query->querycmd);
330     silc_command_set_ident(cmdr->payload,
331                            silc_command_get_ident(query->cmd->payload));
332     buffer = silc_command_payload_encode_payload(cmdr->payload);
333     silc_server_packet_send(server, query->cmd->sock,
334                             SILC_PACKET_COMMAND_REPLY, 0, 
335                             buffer->data, buffer->len, FALSE);
336     silc_buffer_free(buffer);
337     silc_server_query_free(query);
338     return;
339   }
340
341   /* Continue with parsing */
342   silc_server_query_parse(server, query);
343 }
344
345 /* Parse the command query and start processing the queries in detail. */
346
347 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
348 {
349   SilcServerCommandContext cmd = query->cmd;
350   unsigned char *tmp;
351   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
352   void *id;
353   SilcIdType id_type;
354   int i;
355
356   SILC_LOG_DEBUG(("Parsing %s query",
357                   silc_get_command_name(query->querycmd)));
358
359   switch (query->querycmd) {
360
361   case SILC_COMMAND_WHOIS:
362     /* Get Client IDs if present. Take IDs always instead of nickname. */
363     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
364     if (!tmp) {
365
366       /* Get nickname */
367       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
368       if (!tmp) {
369         silc_server_query_send_error(server, query,
370                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
371         silc_server_query_free(query);
372         return;
373       }
374
375       /* Get the nickname@server string and parse it */
376       if (tmp_len > 128 ||
377           !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
378         silc_server_query_send_error(server, query,
379                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
380         silc_server_query_free(query);
381         return;
382       }
383
384     } else {
385       /* Parse the IDs included in the query */
386       query->ids = silc_calloc(argc, sizeof(*query->ids));
387
388       for (i = 0; i < argc; i++) {
389         tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
390         if (!tmp)
391           continue;
392
393         id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
394         if (!id) {
395           silc_server_query_add_error(server, query, TRUE, i + 4,
396                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
397           continue;
398         }
399
400         /* Normal server must check whether this ID exist, and if not then
401            send the query to router, unless done so already */
402         if (server->server_type == SILC_SERVER && !query->resolved) {
403           if (!silc_idlist_find_client_by_id(server->local_list,
404                                              id, TRUE, NULL)) {
405             if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
406                 !silc_idlist_find_client_by_id(server->global_list,
407                                                id, TRUE, NULL)) {
408               silc_server_query_send_router(server, query);
409               for (i = 0; i < query->ids_count; i++)
410                 silc_free(query->ids[i].id);
411               silc_free(query->ids);
412               return;
413             }
414           }
415         }
416
417         query->ids[query->ids_count].id = id;
418         query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
419         query->ids_count++;
420       }
421     }
422
423     /* Get the max count of reply messages allowed */
424     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
425     if (tmp && tmp_len == sizeof(SilcUInt32))
426       SILC_GET32_MSB(query->reply_count, tmp);
427
428     /* Get requested attributes if set */
429     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
430     if (tmp)
431       query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
432     break;
433
434   case SILC_COMMAND_WHOWAS:
435     /* Get nickname */
436     tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
437     if (!tmp) {
438       silc_server_query_send_error(server, query,
439                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
440       silc_server_query_free(query);
441       return;
442     }
443
444     /* Get the nickname@server string and parse it */
445     if (tmp_len > 128 ||
446         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
447       silc_server_query_send_error(server, query,
448                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
449       silc_server_query_free(query);
450       return;
451     }
452
453     /* Get the max count of reply messages allowed */
454     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
455     if (tmp && tmp_len == sizeof(SilcUInt32))
456       SILC_GET32_MSB(query->reply_count, tmp);
457     break;
458
459   case SILC_COMMAND_IDENTIFY:
460     /* Get IDs if present. Take IDs always instead of names. */
461     tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
462     if (!tmp) {
463
464       /* Try get nickname */
465       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
466       if (tmp) {
467         /* Get the nickname@server string and parse it */
468         if (tmp_len > 128 ||
469             !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
470           silc_server_query_add_error(server, query, TRUE, 1,
471                                       SILC_STATUS_ERR_BAD_NICKNAME);
472       }
473
474       /* Try get server name */
475       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
476       if (tmp)
477         query->server_name = silc_memdup(tmp, tmp_len);
478
479       /* Get channel name */
480       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
481       if (tmp && tmp_len <= 256)
482         query->channel_name = silc_memdup(tmp, tmp_len);
483
484     } else {
485       /* Parse the IDs included in the query */
486       query->ids = silc_calloc(argc, sizeof(*query->ids));
487
488       for (i = 0; i < argc; i++) {
489         tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
490         if (!tmp)
491           continue;
492
493         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
494         if (!id) {
495           silc_server_query_add_error(server, query, TRUE, i + 5,
496                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
497           continue;
498         }
499
500         /* Normal server must check whether this ID exist, and if not then
501            send the query to router, unless done so already */
502         if (server->server_type == SILC_SERVER && !query->resolved) {
503           if (!silc_idlist_find_client_by_id(server->local_list,
504                                              id, TRUE, NULL)) {
505             if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
506                 !silc_idlist_find_client_by_id(server->global_list,
507                                                id, TRUE, NULL)) {
508               silc_server_query_send_router(server, query);
509               for (i = 0; i < query->ids_count; i++)
510                 silc_free(query->ids[i].id);
511               silc_free(query->ids);
512               return;
513             }
514           }
515         }
516
517         query->ids[query->ids_count].id = id;
518         query->ids[query->ids_count].id_type = id_type;
519         query->ids_count++;
520       }
521     }
522
523     /* Get the max count of reply messages allowed */
524     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
525     if (tmp && tmp_len == sizeof(SilcUInt32))
526       SILC_GET32_MSB(query->reply_count, tmp);
527     break;
528   }
529
530   /* Start processing the query information */
531   silc_server_query_process(server, query, TRUE);
532 }
533
534 /* Processes the parsed query.  This does the actual finding of the
535    queried information and prepares for sending reply to the original
536    sender of the query command. */
537
538 void silc_server_query_process(SilcServer server, SilcServerQuery query,
539                                bool resolve)
540 {
541   SilcServerCommandContext cmd = query->cmd;
542   bool check_global = FALSE;
543   void *entry;
544   SilcClientEntry *clients = NULL, client_entry;
545   SilcChannelEntry *channels = NULL;
546   SilcServerEntry *servers = NULL;
547   SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
548   int i;
549
550   SILC_LOG_DEBUG(("Processing %s query",
551                   silc_get_command_name(query->querycmd)));
552
553   /* Check global lists if query is coming from client or we are not
554      normal server (we know global information). */
555   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
556     check_global = TRUE;
557   else if (server->server_type != SILC_SERVER)
558     check_global = TRUE;
559
560   if (query->nickname) {
561     /* Get all clients matching nickname from local list */
562     if (!silc_idlist_get_clients_by_hash(server->local_list, 
563                                          query->nickname, server->md5hash,
564                                          &clients, &clients_count))
565       silc_idlist_get_clients_by_nickname(server->local_list, 
566                                           query->nickname,
567                                           query->nick_server,
568                                           &clients, &clients_count);
569
570     /* Check global list as well */
571     if (check_global) {
572       if (!silc_idlist_get_clients_by_hash(server->global_list, 
573                                            query->nickname, server->md5hash,
574                                            &clients, &clients_count))
575         silc_idlist_get_clients_by_nickname(server->global_list, 
576                                             query->nickname,
577                                             query->nick_server,
578                                             &clients, &clients_count);
579     }
580
581     if (!clients)
582       silc_server_query_add_error(server, query, TRUE, 1,
583                                   SILC_STATUS_ERR_NO_SUCH_NICK);
584   }
585
586   if (query->server_name) {
587     /* Find server by name */
588     entry = silc_idlist_find_server_by_name(server->local_list,
589                                             query->server_name, TRUE, NULL);
590     if (!entry && check_global)
591       entry = silc_idlist_find_server_by_name(server->global_list,
592                                               query->server_name, TRUE, NULL);
593     if (entry) {
594       servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
595       servers[servers_count++] = (SilcServerEntry)entry;
596     }
597
598     if (!servers)
599       silc_server_query_add_error(server, query, TRUE, 2,
600                                   SILC_STATUS_ERR_NO_SUCH_SERVER);
601   }
602
603   if (query->channel_name) {
604     /* Find channel by name */
605     entry = silc_idlist_find_channel_by_name(server->local_list,
606                                              query->channel_name, NULL);
607     if (!entry && check_global)
608       entry = silc_idlist_find_channel_by_name(server->global_list,
609                                                query->channel_name, NULL);
610     if (entry) {
611       channels = silc_realloc(channels, sizeof(*channels) *
612                               (channels_count + 1));
613       channels[channels_count++] = (SilcChannelEntry)entry;
614     }
615
616     if (!channels)
617       silc_server_query_add_error(server, query, TRUE, 3,
618                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL);
619   }
620
621   if (query->ids_count) {
622     /* Find entries by the queried IDs */
623     for (i = 0; i < query->ids_count; i++) {
624       void *id = query->ids[i].id;
625       if (!id)
626         continue;
627
628       switch (query->ids[i].id_type) {
629
630       case SILC_ID_CLIENT:
631         /* Get client entry */
632         entry = silc_idlist_find_client_by_id(server->local_list,
633                                               id, TRUE, NULL);
634         if (!entry && check_global)
635           entry = silc_idlist_find_client_by_id(server->global_list,
636                                                 id, TRUE, NULL);
637         if (!entry) {
638           silc_server_query_add_error(server, query, FALSE, i,
639                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
640           continue;
641         }
642
643         clients = silc_realloc(clients, sizeof(*clients) *
644                                (clients_count + 1));
645         clients[clients_count++] = (SilcClientEntry)entry;
646         break;
647
648       case SILC_ID_SERVER:
649         /* Get server entry */
650         entry = silc_idlist_find_server_by_id(server->local_list,
651                                               id, TRUE, NULL);
652         if (!entry && check_global)
653           entry = silc_idlist_find_server_by_id(server->global_list,
654                                                 id, TRUE, NULL);
655         if (!entry) {
656           silc_server_query_add_error(server, query, FALSE, i,
657                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
658           continue;
659         }
660
661         servers = silc_realloc(servers, sizeof(*servers) *
662                                (servers_count + 1));
663         servers[servers_count++] = (SilcServerEntry)entry;
664         break;
665
666       case SILC_ID_CHANNEL:
667         /* Get channel entry */
668         entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
669         if (!entry && check_global)
670           entry = silc_idlist_find_channel_by_id(server->global_list, id,
671                                                  NULL);
672         if (!entry) {
673           silc_server_query_add_error(server, query, FALSE, i,
674                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
675           continue;
676         }
677
678         channels = silc_realloc(channels, sizeof(*channels) *
679                                 (channels_count + 1));
680         channels[channels_count++] = (SilcChannelEntry)entry;
681         break;
682
683       default:
684         break;
685       }
686     }
687   }
688
689   /* If nothing was found, then just send the errors */
690   if (!clients && !channels && !servers) {
691     silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
692     return;
693   }
694
695   /* If caller does not want us to resolve anything (has resolved already)
696      then just continue with sending the reply */
697   if (!resolve) {
698     silc_server_query_send_reply(server, query, clients, clients_count,
699                                  servers, servers_count, channels,
700                                  channels_count);
701     silc_free(clients);
702     silc_free(servers);
703     silc_free(channels);
704     return;
705   }
706
707   /* Now process all found information and if necessary do some more
708      resolving. */
709   switch (query->querycmd) {
710
711   case SILC_COMMAND_WHOIS:
712     for (i = 0; i < clients_count; i++) {
713       client_entry = clients[i];
714
715       /* Check if cannot query this anyway, so take next one */
716       if (!client_entry ||
717           !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
718         continue;
719
720       /* If Requested Attributes is set then we always resolve the client
721          information, if not then check whether the entry is complete or not
722          and decide whether we need to resolve or not. */
723       if (!query->attrs) {
724
725         /* Even if nickname and stuff are present, we may need to resolve
726            the entry */
727         if (client_entry->nickname && client_entry->username &&
728             client_entry->userinfo) {
729           /* Check if cannot query this anyway, so take next one */
730           if (!client_entry->router)
731             continue;
732
733           /* If we are router, client is local to us, or client is on channel
734              we do not need to resolve the client information. */
735           if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
736               || silc_hash_table_count(client_entry->channels) ||
737               query->resolved)
738             continue;
739         }
740       }
741
742       /* When requested attributes is present and local client is detached
743          we cannot send the command to the client, we'll reply on behalf of
744          the client instead. */
745       if (query->attrs && SILC_IS_LOCAL(client_entry) &&
746           (client_entry->mode & SILC_UMODE_DETACHED ||
747            client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
748         continue;
749
750       /* Resolve the detailed client information. If client is local we
751          know that attributes were present and we will resolve directly
752          from the client. Otherwise resolve from client's owner. */
753       silc_server_query_resolve(server, query,
754                                 (SILC_IS_LOCAL(client_entry) ?
755                                  client_entry->connection :
756                                  client_entry->router->connection),
757                                 client_entry);
758     }
759     break;
760
761   case SILC_COMMAND_WHOWAS:
762     for (i = 0; i < clients_count; i++) {
763       client_entry = clients[i];
764
765       /* Check if cannot query this anyway, so take next one */
766       if (!client_entry || !client_entry->router ||
767           client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
768         continue;
769
770       /* If both nickname and username are present no resolving is needed */
771       if (client_entry->nickname && client_entry->username)
772         continue;
773
774       /* Resolve the detailed client information */
775       silc_server_query_resolve(server, query,
776                                 client_entry->router->connection,
777                                 client_entry);
778     }
779     break;
780
781   case SILC_COMMAND_IDENTIFY:
782     for (i = 0; i < clients_count; i++) {
783       client_entry = clients[i];
784
785       /* Check if cannot query this anyway, so take next one */
786       if (!client_entry || !client_entry->router ||
787           !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
788         continue;
789
790       /* Even if nickname is present, we may need to resolve the entry */
791       if (client_entry->nickname) {
792
793         /* If we are router, client is local to us, or client is on channel
794            we do not need to resolve the client information. */
795         if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
796             || silc_hash_table_count(client_entry->channels) ||
797             query->resolved)
798           continue;
799       }
800
801       /* Resolve the detailed client information */
802       silc_server_query_resolve(server, query,
803                                 client_entry->router->connection,
804                                 client_entry);
805     }
806     break;
807   }
808
809   if (!query->queries_count)
810     /* If we didn't have to do any resolving, continue with sending the
811        command reply to the original sender. */
812     silc_server_query_send_reply(server, query, clients, clients_count,
813                                  servers, servers_count, channels,
814                                  channels_count);
815   else
816     /* Now actually send the resolvings we gathered earlier */
817     silc_server_query_resolve(server, query, NULL, NULL);
818
819   silc_free(clients);
820   silc_free(servers);
821   silc_free(channels);
822 }
823
824 /* Resolve the detailed information for the `client_entry'.  Only client
825    information needs to be resolved for being incomplete.  Each incomplete
826    client entry calls this function to do the resolving. */
827
828 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
829                                SilcSocketConnection sock,
830                                SilcClientEntry client_entry)
831 {
832   SilcServerCommandContext cmd = query->cmd;
833   SilcServerQueryList r = NULL;
834   SilcBuffer idp;
835   unsigned char *tmp;
836   SilcUInt32 len;
837   SilcUInt16 ident;
838   int i;
839
840   if (!sock && client_entry)
841     return;
842
843   /* If arguments are NULL we will now actually send the resolvings
844      that earlier has been gathered by calling this function. */
845   if (!sock && !client_entry) {
846     SilcBuffer res_cmd;
847
848     SILC_LOG_DEBUG(("Sending the resolvings"));
849
850     /* WHOWAS resolving has been done at the same time this function
851        was called to add the resolving for WHOWAS, so just return. */
852     if (query->querycmd == SILC_COMMAND_WHOWAS)
853       return;
854
855     for (i = 0; i < query->querylist_count; i++) {
856       r = &query->querylist[i];
857
858       /* Send WHOIS command */
859       res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
860                                             r->argc, r->arg, r->arg_lens,
861                                             r->arg_types, r->ident);
862       silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0,
863                               res_cmd->data, res_cmd->len, FALSE);
864       silc_buffer_free(res_cmd);
865
866       /* Reprocess this packet after received reply */
867       if (silc_server_command_pending_timed(server, SILC_COMMAND_WHOIS,
868                                             r->ident,
869                                             silc_server_query_resolve_reply,
870                                             query, r->timeout))
871         query->queries_left++;
872     }
873
874     /* Cleanup this temporary context */
875     for (i = 0; i < query->querylist_count; i++) {
876       int k;
877       for (k = 0; k < query->querylist[i].argc; k++)
878         silc_free(query->querylist[i].arg[k]);
879       silc_free(query->querylist[i].arg);
880       silc_free(query->querylist[i].arg_lens);
881       silc_free(query->querylist[i].arg_types);
882     }
883     silc_free(query->querylist);
884     query->querylist = NULL;
885     query->querylist_count = 0;
886     return;
887   }
888
889   SILC_LOG_DEBUG(("Resolving client information"));
890
891   if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
892     /* The entry is being resolved by some other external query already.
893        Attach to that query instead of resolving again. */
894     ident = client_entry->resolve_cmd_ident;
895     if (silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
896                                     silc_server_query_resolve_reply, query))
897       query->queries_left++;
898   } else {
899     /* This entry will be resolved */
900     ident = ++server->cmd_ident;
901
902     switch (query->querycmd) {
903
904     case SILC_COMMAND_WHOIS:
905     case SILC_COMMAND_IDENTIFY:
906       /* Take existing query context if exist for this connection */
907       for (i = 0; i < query->queries_count; i++)
908         if (query->querylist[i].sock == sock) {
909           r = &query->querylist[i];
910           break;
911         }
912
913       if (!r) {
914         /* Allocate new temp query list context */
915         query->querylist = silc_realloc(query->querylist,
916                                         sizeof(*query->querylist) * 
917                                         (query->querylist_count + 1));
918         r = &query->querylist[query->querylist_count];
919         query->querylist_count++;
920         memset(r, 0, sizeof(*r));
921         r->sock = sock;
922         r->ident = ident;
923         if (SILC_IS_LOCAL(client_entry))
924           r->timeout = 3;
925       }
926
927       /* If Requested Attributes were present put them to this resolving */
928       if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
929         len = r->argc + 1;
930         r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
931         r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
932         r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
933
934         tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
935         if (tmp)
936           r->arg[r->argc] = silc_memdup(tmp, len);
937         r->arg_lens[r->argc] = len;
938         r->arg_types[r->argc] = 3;
939         r->argc++;
940       }
941
942       len = r->argc + 1;
943       r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
944       r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
945       r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
946
947       /* Add the client entry to be resolved */
948       idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
949       r->arg[r->argc] = silc_memdup(idp->data, idp->len);
950       r->arg_lens[r->argc] = idp->len;
951       r->arg_types[r->argc] = r->argc + 4;
952       r->argc++;
953       silc_buffer_free(idp);
954
955       break;
956
957     case SILC_COMMAND_WHOWAS:
958       /* We must send WHOWAS command since it's the only the way of
959          resolving clients that are not present in the network anymore. */
960       silc_server_send_command(server, sock, query->querycmd, ident, 1,
961                                1, query->nickname, strlen(query->nickname));
962       if (silc_server_command_pending(server, query->querycmd, ident,
963                                       silc_server_query_resolve_reply, query))
964         query->queries_left++;
965       break;
966     }
967   }
968
969   /* Mark the entry as being resolved */
970   client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
971   client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
972   client_entry->resolve_cmd_ident = ident;
973
974   /* Save the queried ID, which we will reprocess after we get this and
975      all other queries back. */
976   query->queries = silc_realloc(query->queries, sizeof(*query->queries) *
977                                 (query->queries_count + 1));
978   if (query->queries) {
979     i = query->queries_count;
980     query->queries[i].id = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
981     query->queries[i].id_type = SILC_ID_CLIENT;
982     query->queries[i].ident = ident;
983     query->queries_count++;
984   }
985 }
986
987 /* Reply callback called after one resolving has been completed.  If
988    all resolvings has been received then we will continue with sending
989    the command reply to the original sender of the query. */
990
991 void silc_server_query_resolve_reply(void *context, void *reply)
992 {
993   SilcServerQuery query = context;
994   SilcServer server = query->cmd->server;
995   SilcServerCommandReplyContext cmdr = reply;
996   SilcUInt16 ident = cmdr->ident;
997   SilcStatus error = SILC_STATUS_OK;
998   SilcServerQueryID id = NULL;
999   SilcClientEntry client_entry;
1000   int i;
1001
1002   /* One less query left */
1003   query->queries_left--;
1004
1005   silc_command_get_status(cmdr->payload, NULL, &error);
1006   SILC_LOG_DEBUG(("Received reply to resolving (%d left) (status=%d)",
1007                   query->queries_left, error));
1008
1009   /* If no error then skip to other stuff */
1010   if (error == SILC_STATUS_OK)
1011     goto out;
1012
1013   /* Error occurred during resolving */
1014
1015   /* Find the resolved client ID */
1016   for (i = 0; i < query->queries_count; i++) {
1017     if (query->queries[i].ident != ident)
1018       continue;
1019
1020     id = &query->queries[i];
1021
1022     if (error == SILC_STATUS_ERR_TIMEDOUT) {
1023       /* If timeout occurred for local entry when resolving attributes
1024          mark that this client doesn't support attributes in WHOIS. This
1025          assures we won't send the request again to the client. */
1026       if (query->querycmd == SILC_COMMAND_WHOIS && query->attrs) {
1027         client_entry = silc_idlist_find_client_by_id(server->local_list,
1028                                                      id->id, TRUE, NULL);
1029         SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
1030                         silc_id_render(id->id, SILC_ID_CLIENT)));
1031         if (client_entry && SILC_IS_LOCAL(client_entry))
1032           client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
1033       }
1034     }
1035   }
1036
1037  out:
1038
1039   /* If there are queries left then wait for them */
1040   if (query->queries_left)
1041     return;
1042
1043   SILC_LOG_DEBUG(("Reprocess the query"));
1044
1045   /* We have received all queries.  Now re-search all information required
1046      to complete this query.  Reason we cannot save the values found in
1047      the first search is that SilcClientEntry, SilcServerEntry and
1048      SilcChannelEntry pointers may become invalid while we were waiting
1049      for these resolvings. */
1050   silc_server_query_process(server, query, FALSE);
1051 }
1052
1053 /* Send the reply to the original query.  If arguments are NULL then this
1054    sends only the errors that has occurred during the processing of the
1055    query.  This sends the errors always after sending all the found
1056    information.  The query is over after this function returns and the
1057    `query' will become invalid.  This is called only after all informations
1058    has been resolved.  This means that if something is not found or is
1059    incomplete in this function we were unable to resolve the information
1060    or it does not exist at all. */
1061
1062 void silc_server_query_send_reply(SilcServer server,
1063                                   SilcServerQuery query,
1064                                   SilcClientEntry *clients,
1065                                   SilcUInt32 clients_count,
1066                                   SilcServerEntry *servers,
1067                                   SilcUInt32 servers_count,
1068                                   SilcChannelEntry *channels,
1069                                   SilcUInt32 channels_count)
1070 {
1071   SilcServerCommandContext cmd = query->cmd;
1072   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
1073   SilcStatus status;
1074   unsigned char *tmp;
1075   SilcUInt32 len;
1076   SilcBuffer idp;
1077   int i, k, valid_count;
1078   char nh[256], uh[256];
1079   bool sent_reply = FALSE;
1080
1081   SILC_LOG_DEBUG(("Sending reply to query"));
1082
1083   status = SILC_STATUS_OK;
1084
1085   /* Send clients */
1086   if (clients_count) {
1087     SilcClientEntry entry;
1088     SilcSocketConnection hsock;
1089
1090     /* Mark all invalid entries */
1091     for (i = 0, valid_count = 0; i < clients_count; i++) {
1092       entry = clients[i];
1093       switch (query->querycmd) {
1094       case SILC_COMMAND_WHOIS:
1095         if (!entry->nickname || !entry->username || !entry->userinfo ||
1096             !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1097           /* When querying by ID, every "unfound" entry must cause error */
1098           if (query->ids)
1099             silc_server_query_add_error_id(server, query,
1100                                            SILC_STATUS_ERR_TIMEDOUT,
1101                                            entry->id, SILC_ID_CLIENT);
1102           clients[i] = NULL;
1103           continue;
1104         }
1105         break;
1106
1107       case SILC_COMMAND_IDENTIFY:
1108         if (!entry->nickname ||
1109             !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1110           /* When querying by ID, every "unfound" entry must cause error */
1111           if (query->ids)
1112             silc_server_query_add_error_id(server, query,
1113                                            SILC_STATUS_ERR_TIMEDOUT,
1114                                            entry->id, SILC_ID_CLIENT);
1115           clients[i] = NULL;
1116           continue;
1117         }
1118         break;
1119
1120       case SILC_COMMAND_WHOWAS:
1121         if (!entry->nickname || !entry->username ||
1122             entry->data.status & SILC_IDLIST_STATUS_REGISTERED) {
1123           clients[i] = NULL;
1124           continue;
1125         }
1126         break;
1127       }
1128       valid_count++;
1129     }
1130
1131     /* Start processing found clients */
1132     status = SILC_STATUS_OK;
1133     if (valid_count > 1)
1134       status = SILC_STATUS_LIST_START;
1135
1136     /* Now do the sending of valid entries */
1137     k = 0;
1138     for (i = 0; i < clients_count && valid_count; i++) {
1139       entry = clients[i];
1140       if (!entry)
1141         continue;
1142
1143       if (k >= 1)
1144         status = SILC_STATUS_LIST_ITEM;
1145       if (valid_count > 1 && k == valid_count - 1 
1146           && !servers_count && !channels_count && !query->errors_count)
1147         status = SILC_STATUS_LIST_END;
1148       if (query->reply_count && k - 1 == query->reply_count)
1149         status = SILC_STATUS_LIST_END;
1150
1151       SILC_LOG_DEBUG(("%s: client %s",
1152                       (status == SILC_STATUS_OK ?         "   OK" :
1153                        status == SILC_STATUS_LIST_START ? "START" :
1154                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1155                        status == SILC_STATUS_LIST_END  ?  "  END" :
1156                        "      : "), entry->nickname));
1157
1158       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
1159       memset(uh, 0, sizeof(uh));
1160       memset(nh, 0, sizeof(nh));
1161
1162       silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
1163       if (!strchr(entry->nickname, '@')) {
1164         silc_strncat(nh, sizeof(nh), "@", 1);
1165         if (entry->servername) {
1166           silc_strncat(nh, sizeof(nh), entry->servername,
1167                        strlen(entry->servername));
1168         } else {
1169           len = entry->router ? strlen(entry->router->server_name) :
1170             strlen(server->server_name);
1171           silc_strncat(nh, sizeof(nh), entry->router ?
1172                        entry->router->server_name :
1173                        server->server_name, len);
1174         }
1175       }
1176       
1177       switch (query->querycmd) {
1178         
1179       case SILC_COMMAND_WHOIS:
1180         {
1181           unsigned char idle[4], mode[4];
1182           unsigned char *fingerprint, fempty[20], *attrs;
1183           SilcBuffer channels, umode_list = NULL;
1184
1185           memset(fempty, 0, sizeof(fempty));
1186           silc_strncat(uh, sizeof(uh), entry->username,
1187                        strlen(entry->username));
1188           if (!strchr(entry->username, '@') && entry->connection) {
1189             hsock = entry->connection;
1190             silc_strncat(uh, sizeof(uh), "@", 1);
1191             len = strlen(hsock->hostname);
1192             silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1193           }
1194
1195           if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
1196             channels =
1197               silc_server_get_client_channel_list(server, entry, FALSE, 
1198                                                   FALSE, &umode_list);
1199           else
1200             channels =
1201               silc_server_get_client_channel_list(server, entry, TRUE, 
1202                                                   TRUE, &umode_list);
1203
1204           if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
1205             fingerprint = entry->data.fingerprint;
1206           else
1207             fingerprint = NULL;
1208
1209           SILC_PUT32_MSB(entry->mode, mode);
1210           if (entry->connection)
1211             SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
1212
1213           /* If Requested Attribute were present, and we do not have the
1214              attributes we will reply to them on behalf of the client. */
1215           if (query->attrs) {
1216             if (!entry->attrs) {
1217               attrs = silc_server_query_reply_attrs(server, query, &len);
1218             } else {
1219               attrs = entry->attrs;
1220               len = entry->attrs_len;
1221             }
1222           }
1223
1224           /* Send command reply */
1225           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1226                                          status, 0, ident, 9,
1227                                          2, idp->data, idp->len,
1228                                          3, nh, strlen(nh),
1229                                          4, uh, strlen(uh),
1230                                          5, entry->userinfo, 
1231                                          strlen(entry->userinfo),
1232                                          6, channels ? channels->data : NULL,
1233                                          channels ? channels->len : 0,
1234                                          7, mode, 4,
1235                                          8, idle, 4,
1236                                          9, fingerprint,
1237                                          fingerprint ? 20 : 0,
1238                                          10, umode_list ? umode_list->data :
1239                                          NULL, umode_list ? umode_list->len :
1240                                          0);
1241           sent_reply = TRUE;
1242
1243           /* For now we will delete Requested Attributes */
1244           silc_free(entry->attrs);
1245           entry->attrs = NULL;
1246
1247           if (channels)
1248             silc_buffer_free(channels);
1249           if (umode_list) {
1250             silc_buffer_free(umode_list);
1251             umode_list = NULL;
1252           }
1253         }
1254         break;
1255
1256       case SILC_COMMAND_IDENTIFY:
1257         if (!entry->username) {
1258           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1259                                          status, 0, ident, 2,
1260                                          2, idp->data, idp->len,
1261                                          3, nh, strlen(nh));
1262           sent_reply = TRUE;
1263         } else {
1264           silc_strncat(uh, sizeof(uh), entry->username,
1265                        strlen(entry->username));
1266           if (!strchr(entry->username, '@') && entry->connection) {
1267             hsock = entry->connection;
1268             silc_strncat(uh, sizeof(uh), "@", 1);
1269             len = strlen(hsock->hostname);
1270             silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1271           }
1272
1273           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1274                                          status, 0, ident, 3,
1275                                          2, idp->data, idp->len,
1276                                          3, nh, strlen(nh),
1277                                          4, uh, strlen(uh));
1278           sent_reply = TRUE;
1279         }
1280         break;
1281
1282       case SILC_COMMAND_WHOWAS:
1283         silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
1284         if (!strchr(entry->username, '@'))
1285           silc_strncat(uh, sizeof(uh), "@*private*", 10);
1286
1287         /* Send command reply */
1288         silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1289                                        status, 0, ident, 4,
1290                                        2, idp->data, idp->len,
1291                                        3, nh, strlen(nh),
1292                                        4, uh, strlen(uh),
1293                                        5, entry->userinfo, 
1294                                        entry->userinfo ? 
1295                                        strlen(entry->userinfo) : 0);
1296         sent_reply = TRUE;
1297         break;
1298       }
1299
1300       silc_buffer_free(idp);
1301
1302       if (status == SILC_STATUS_LIST_END)
1303         break;
1304       k++;
1305     }
1306
1307     if (k == 0) {
1308       /* Not one valid entry was found, send error.  If nickname was used
1309          in query send error based on that, otherwise the query->errors
1310          already includes proper errors. */
1311       if (query->nickname)
1312         silc_server_query_add_error(server, query, TRUE, 1,
1313                                     SILC_STATUS_ERR_NO_SUCH_NICK);
1314     }
1315   }
1316
1317   /* Send servers */
1318   if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
1319     SilcServerEntry entry;
1320
1321     if (status == SILC_STATUS_OK && servers_count > 1)
1322       status = SILC_STATUS_LIST_START;
1323
1324     k = 0;
1325     for (i = 0; i < servers_count; i++) {
1326       entry = servers[i];
1327       
1328       if (k >= 1)
1329         status = SILC_STATUS_LIST_ITEM;
1330       if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
1331           !query->errors_count)
1332         status = SILC_STATUS_LIST_END;
1333       if (query->reply_count && k - 1 == query->reply_count)
1334         status = SILC_STATUS_LIST_END;
1335       
1336       SILC_LOG_DEBUG(("%s: server %s",
1337                       (status == SILC_STATUS_OK ?         "   OK" :
1338                        status == SILC_STATUS_LIST_START ? "START" :
1339                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1340                        status == SILC_STATUS_LIST_END  ?  "  END" :
1341                        "      : "),
1342                       entry->server_name ? entry->server_name : ""));
1343
1344       /* Send command reply */
1345       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
1346       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1347                                      status, 0, ident, 2,
1348                                      2, idp->data, idp->len, 
1349                                      3, entry->server_name, 
1350                                      entry->server_name ? 
1351                                      strlen(entry->server_name) : 0);
1352       silc_buffer_free(idp);
1353       sent_reply = TRUE;
1354       
1355       if (status == SILC_STATUS_LIST_END)
1356         break;
1357       k++;
1358     }
1359   }
1360
1361   /* Send channels */
1362   if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
1363     SilcChannelEntry entry;
1364
1365     if (status == SILC_STATUS_OK && channels_count > 1)
1366       status = SILC_STATUS_LIST_START;
1367
1368     k = 0;
1369     for (i = 0; i < channels_count; i++) {
1370       entry = channels[i];
1371       
1372       if (k >= 1)
1373         status = SILC_STATUS_LIST_ITEM;
1374       if (channels_count > 1 && k == channels_count - 1 &&
1375           !query->errors_count)
1376         status = SILC_STATUS_LIST_END;
1377       if (query->reply_count && k - 1 == query->reply_count)
1378         status = SILC_STATUS_LIST_END;
1379       
1380       SILC_LOG_DEBUG(("%s: channel %s",
1381                       (status == SILC_STATUS_OK ?         "   OK" :
1382                        status == SILC_STATUS_LIST_START ? "START" :
1383                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1384                        status == SILC_STATUS_LIST_END  ?  "  END" :
1385                        "      : "),
1386                       entry->channel_name ? entry->channel_name : ""));
1387
1388       /* Send command reply */
1389       idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
1390       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1391                                      status, 0, ident, 2,
1392                                      2, idp->data, idp->len, 
1393                                      3, entry->channel_name, 
1394                                      entry->channel_name ? 
1395                                      strlen(entry->channel_name) : 0);
1396       silc_buffer_free(idp);
1397       sent_reply = TRUE;
1398       
1399       if (status == SILC_STATUS_LIST_END)
1400         break;
1401       k++;
1402     }
1403   }
1404
1405   /* Send errors */
1406   if (query->errors_count) {
1407     int type;
1408
1409     if (status == SILC_STATUS_OK && query->errors_count > 1)
1410       status = SILC_STATUS_LIST_START;
1411
1412     k = 0;
1413     for (i = 0; i < query->errors_count; i++) {
1414       idp = NULL;
1415
1416       /* Take error argument */
1417       if (query->errors[i].from_cmd) {
1418         tmp = silc_argument_get_arg_type(cmd->args,
1419                                          query->errors[i].index, &len);
1420         if (query->errors[i].index == 1)
1421           type = 3;                 /* Nickname */
1422         else
1423           type = 2;                 /* ID */
1424       } else if (!query->errors[i].id) {
1425         idp =
1426           silc_id_payload_encode(query->ids[query->errors[i].index].id,
1427                                  query->ids[query->errors[k].index].id_type);
1428         tmp = idp->data;
1429         len = idp->len;
1430         type = 2;
1431       } else {
1432         idp = silc_id_payload_encode(query->errors[i].id,
1433                                      query->errors[k].id_type);
1434         tmp = idp->data;
1435         len = idp->len;
1436         type = 2;
1437       }
1438
1439       if (k >= 1)
1440         status = SILC_STATUS_LIST_ITEM;
1441       if (query->errors_count > 1 && k == query->errors_count - 1)
1442         status = SILC_STATUS_LIST_END;
1443       if (query->reply_count && k - 1 == query->reply_count)
1444         status = SILC_STATUS_LIST_END;
1445
1446       SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
1447                       (status == SILC_STATUS_OK ?         "   OK" :
1448                        status == SILC_STATUS_LIST_START ? "START" :
1449                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1450                        status == SILC_STATUS_LIST_END  ?  "  END" :
1451                        "      : "), 
1452                       silc_get_status_message(query->errors[i].error),
1453                       query->errors[i].error));
1454
1455       /* Send error */
1456       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1457                                      (status == SILC_STATUS_OK ?
1458                                       query->errors[i].error : status),
1459                                      (status == SILC_STATUS_OK ?
1460                                       0 : query->errors[i].error), ident, 1,
1461                                      type, tmp, len);
1462       silc_buffer_free(idp);
1463       sent_reply = TRUE;
1464
1465       if (status == SILC_STATUS_LIST_END)
1466         break;
1467       k++;
1468     }
1469   }
1470
1471   if (!sent_reply)
1472     SILC_LOG_ERROR(("BUG: Query did not send anything"));
1473
1474   /* Cleanup */
1475   silc_server_query_free(query);
1476 }
1477
1478 /* This routine is used to reply to Requested Attributes in WHOIS on behalf
1479    of the client since we were unable to resolve them from the client.
1480    Either client does not support Requested Attributes or isn't replying
1481    to them like it should. */
1482
1483 unsigned char *silc_server_query_reply_attrs(SilcServer server,
1484                                              SilcServerQuery query,
1485                                              SilcUInt32 *attrs_len)
1486 {
1487   unsigned char *attrs = NULL;
1488   SilcUInt32 len = 0;
1489
1490   SILC_LOG_DEBUG(("Constructing Requested Attributes"));
1491
1492   if (attrs_len)
1493     *attrs_len = len;
1494
1495   return attrs;
1496 }
1497
1498 /* Find client by the Client ID indicated by the `client_id', and if not
1499    found then query it by using WHOIS command.  The client information
1500    is also resolved if the cached information is incomplete or if the
1501    `always_resolve' is set to TRUE.  The indication whether requested
1502    client was being resolved is saved into `resolved'.  If the client
1503    is not being resolved its entry is returned by this function.  NULL
1504    is returned if client is resolved. */
1505
1506 SilcClientEntry silc_server_query_client(SilcServer server,
1507                                          const SilcClientID *client_id,
1508                                          bool always_resolve,
1509                                          bool *resolved)
1510 {
1511   SilcClientEntry client;
1512
1513   SILC_LOG_DEBUG(("Resolving client by client ID"));
1514
1515   if (resolved)
1516     *resolved = FALSE;
1517
1518   client = silc_idlist_find_client_by_id(server->local_list,
1519                                          (SilcClientID *)client_id,
1520                                          TRUE, NULL);
1521   if (!client) {
1522     client = silc_idlist_find_client_by_id(server->global_list,
1523                                            (SilcClientID *)client_id,
1524                                            TRUE, NULL);
1525     if (!client && server->server_type == SILC_ROUTER)
1526       return NULL;
1527   }
1528
1529   if (!client && server->standalone)
1530     return NULL;
1531
1532   if (!client || !client->nickname || !client->username ||
1533       always_resolve) {
1534     SilcBuffer buffer, idp;
1535
1536     if (client) {
1537       client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
1538       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
1539       client->resolve_cmd_ident = ++server->cmd_ident;
1540     }
1541
1542     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
1543     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
1544                                             server->cmd_ident, 1,
1545                                             4, idp->data, idp->len);
1546     silc_server_packet_send(server, client ? client->router->connection :
1547                             SILC_PRIMARY_ROUTE(server),
1548                             SILC_PACKET_COMMAND, 0,
1549                             buffer->data, buffer->len, FALSE);
1550     silc_buffer_free(idp);
1551     silc_buffer_free(buffer);
1552
1553     if (resolved)
1554       *resolved = TRUE;
1555
1556     return NULL;
1557   }
1558
1559   return client;
1560 }