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