More new query code.
[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 #include "serverincludes.h"
22 #include "server_internal.h"
23
24 /* Represents an SILC ID */
25 typedef struct {
26   void *id;                         /* ID */
27   SilcIdType id_type;               /* ID type */
28 } *SilcServerQueryID;
29
30 /* Represents one error occurred during query */
31 typedef struct {
32   SilcUInt32 index;                 /* Index to IDs */
33   bool from_cmd;                    /* TRUE if `index' is from command args,
34                                        otherwise from query->ids */
35   SilcStatus error;                 /* The actual error */
36 } *SilcServerQueryError;
37
38 /* Context for ongoing queries that server the original query (like to
39    resolve detailed information from some other server before replying
40    to the original sender). */
41 typedef struct {
42   unsigned char **arg;              /* Query argument */
43   SilcUInt32 *arg_lens;             /* Query argument lengths */
44   SilcUInt32 *arg_types;            /* Query argument types */
45   SilcUInt32 argc;                  /* Number of query arguments */
46   SilcUInt32 timeout;               /* Max timeout for query to complete */
47   SilcUInt16 ident;                 /* Query command identifier */
48 } *SilcServerQueryList;
49
50 /* Query session context */
51 typedef struct {
52   /* Query session data */
53   SilcCommand querycmd;             /* Query command */
54   SilcServerCommandContext cmd;     /* Command context for query */
55   SilcServerQueryList queries;      /* Ongoing queries */
56   SilcUInt32 num_query;             /* Number of ongoing queries left */
57
58   /* Queried data */
59   char *nickname;                   /* Queried nickname */
60   char *nick_server;                /* Queried nickname's server */
61   char *server_name;                /* Queried server name */
62   char *channel_name;               /* Queried channel name */
63   SilcServerQueryID ids;            /* Queried IDs */
64   SilcUInt32 ids_count;             /* number of queried IDs */
65   SilcUInt32 reply_count;           /* Requested reply count */
66   SilcDList attrs;                  /* Requested Attributes in WHOIS */
67
68   SilcServerQueryError errors;      /* Query errors */
69   SilcUInt32 errors_count;          /* number of errors */
70 } *SilcServerQuery;
71
72 void silc_server_query_free(SilcServerQuery query);
73 bool silc_server_query_check_error(SilcServer server,
74                                    SilcServerQuery query,
75                                    SilcServerCommandReplyContext cmdr);
76 void silc_server_query_send_error(SilcServer server,
77                                   SilcServerQuery query,
78                                   SilcStatus error, ...);
79 void silc_server_query_add_error(SilcServer server,
80                                  SilcServerQuery query,
81                                  bool from_cmd,
82                                  SilcUInt32 index,
83                                  SilcStatus error);
84 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
85 void silc_server_query_send_router_reply(void *context, void *reply);
86 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
87 void silc_server_query_process(SilcServer server, SilcServerQuery query);
88 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
89                                SilcSocketConnection sock,
90                                SilcClientEntry client_entry);
91 void silc_server_query_resolve_reply(void *context, void *reply);
92 void silc_server_query_send_reply(SilcServer server,
93                                   SilcServerQuery query,
94                                   SilcClientEntry *clients,
95                                   SilcUInt32 clients_count,
96                                   SilcServerEntry *servers,
97                                   SilcUInt32 servers_count,
98                                   SilcChannelEntry *channels,
99                                   SilcUInt32 channels_count);
100
101 /* Free the query context structure and all allocated resources. */
102
103 void silc_server_query_free(SilcServerQuery query)
104 {
105   int i;
106
107   silc_server_command_free(query->cmd);
108
109   silc_free(query->nickname);
110   silc_free(query->nick_server);
111   silc_free(query->server_name);
112   silc_free(query->channel_name);
113
114   for (i = 0; i < query->ids_count; i++)
115     silc_free(query->ids[i].id);
116   silc_free(query->ids);
117
118   if (query->attrs)
119     silc_attribute_payload_list_free(query->attrs);
120
121   silc_free(query->errors);
122
123   memset(query, 'F', sizeof(*query));
124   silc_free(query);
125 }
126
127 /* Check whether command reply contained error, and reply the error to
128    the original sender if it occurred. */
129
130 bool silc_server_query_check_error(SilcServer server,
131                                    SilcServerQuery query,
132                                    SilcServerCommandReplyContext cmdr)
133 {
134   if (!cmdr)
135     return FALSE;
136
137   if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
138     SilcBuffer buffer;
139
140     /* Send the same command reply payload which contains the error */
141     silc_command_set_command(cmdr->payload, query->querycmd);
142     silc_command_set_ident(cmdr->payload,
143                            silc_command_get_ident(query->cmd->payload));
144     buffer = silc_command_payload_encode_payload(cmdr->payload);
145     silc_server_packet_send(server, query->cmd->sock,
146                             SILC_PACKET_COMMAND_REPLY, 0, 
147                             buffer->data, buffer->len, FALSE);
148     silc_buffer_free(buffer);
149     return TRUE;
150   }
151
152   return FALSE;
153 }
154
155 /* Send error reply indicated by the `error' to the original sender of
156    the query. */
157
158 void silc_server_query_send_error(SilcServer server,
159                                   SilcServerQuery query,
160                                   SilcStatus error, ...)
161 {
162   va_list va;
163   unsigned char *data = NULL;
164   SilcUInt32 data_len = 0, data_type = 0, argc = 0;
165
166   va_start(va, error);
167   data_type = va_arg(va, SilcUInt32);
168   if (data_type) {
169     argc = 1;
170     data = va_arg(va, unsigned char *);
171     data_len = va_arg(va, SilcUInt32);
172   }
173
174   /* Send the command reply with error */
175   silc_server_send_command_reply(server, query->cmd->sock,
176                                  query->querycmd, error, 0, 
177                                  silc_command_get_ident(query->cmd->payload),
178                                  argc, data_type, data, data_len);
179   va_end(va);
180 }
181
182 /* Add error to error list.  Multiple errors may occur during the query
183    processing and this function can be used to add one error.  The
184    `index' is the index to the command context which includes the argument
185    which caused the error, or it is the index to query->ids, depending
186    on value of `from_cmd'. */
187
188 void silc_server_query_add_error(SilcServer server,
189                                  SilcServerQuery query,
190                                  bool from_cmd,
191                                  SilcUInt32 index,
192                                  SilcStatus error)
193 {
194   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
195                                (query->errors_count + 1));
196   if (!query->errors)
197     return;
198   query->errors[query->errors_count].index = index;
199   query->errors[query->errors_count].from_cmd = from_cmd;
200   query->errors[query->errors_count].error = error;
201   query->errors_count++;
202 }
203
204 /* Processes query as command.  The `query' is the command that is
205    being processed indicated by the `cmd'.  The `query' can be one of
206    the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
207    SILC_COMMAND_IDENTIFY.  This function handles the reply sending
208    to the entity who sent this query to us automatically.  Returns
209    TRUE if the query is being processed or FALSE on error. */
210
211 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
212                                SilcServerCommandContext cmd)
213 {
214   SilcServerQuery query;
215
216   query = silc_calloc(1, sizeof(*query));
217   query->querycmd = querycmd;
218   query->cmd = silc_server_command_dup(cmd);
219
220   switch (querycmd) {
221
222   case SILC_COMMAND_WHOIS:
223     /* If we are normal server and query contains nickname, send it
224        directly to router. */
225     if (server->server_type == SILC_SERVER && !server->standalone &&
226         silc_argument_get_arg_type(cmd->args, 1, NULL)) {
227       silc_server_query_send_router(server, query);
228       break;
229     }
230     break;
231
232   case SILC_COMMAND_WHOWAS:
233     /* WHOWAS query is always sent to router if we are normal server */
234     if (server->server_type == SILC_SERVER && !server->standalone) {
235       silc_server_query_send_router(server, query);
236       break;
237     }
238     break;
239
240   case SILC_COMMAND_IDENTIFY:
241     /* If we are normal server and query does not contain IDs, send it
242        directly to router (it contains nickname, server name or channel
243        name). */
244     if (server->server_type == SILC_SERVER && !server->standalone &&
245         !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
246       silc_server_query_send_router(server, query);
247       break;
248     }
249     break;
250
251   default:
252     SILC_LOG_ERROR(("Bad query using %d command", querycmd));
253     silc_server_query_free(query);
254     return FALSE;
255   }
256
257   /* Now parse the request */
258   silc_server_query_parse(server, query);
259
260   return TRUE;
261 }
262
263 /* Send the received query to our primary router since we could not
264    handle the query directly.  We will reprocess the query after our
265    router replies back. */
266
267 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
268 {
269   SilcBuffer tmpbuf;
270   SilcUInt16 old_ident;
271
272   /* Send WHOIS command to our router */
273   old_ident = silc_command_get_ident(query->cmd->payload);
274   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
275   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
276   silc_server_packet_send(server, 
277                           SILC_PRIMARY_ROUTE(server),
278                           SILC_PACKET_COMMAND, 0,
279                           tmpbuf->data, tmpbuf->len, TRUE);
280   silc_command_set_ident(query->cmd->payload, old_ident);
281   silc_buffer_free(tmpbuf);
282
283   /* Continue parsing the query after received reply from router */
284   silc_server_command_pending(server, query->querycmd, server->cmd_ident,
285                               silc_server_query_send_router_reply, query);
286 }
287
288 /* Reply callback called after primary router has replied to our initial
289    sending of the query to it.  We will proceed the query in this function. */
290
291 void silc_server_query_send_router_reply(void *context, void *reply)
292 {
293   SilcServerQuery query = context;
294   SilcServer server = query->cmd->server;
295
296   /* Check if router sent error reply */
297   if (!silc_server_query_check_error(server, query, reply)) {
298     silc_server_query_free(query);
299     return;
300   }
301
302   /* Continue with parsing */
303   silc_server_query_parse(server, query);
304 }
305
306 /* Parse the command query and start processing the queries in detail. */
307
308 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
309 {
310   SilcServerCommandContext cmd = query->cmd;
311   unsigned char *tmp;
312   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
313   void *id;
314   SilcIdType id_type;
315   int i;
316
317   switch (query->querycmd) {
318
319   case SILC_COMMAND_WHOIS:
320     /* Get Client IDs if present. Take IDs always instead of nickname. */
321     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
322     if (!tmp) {
323
324       /* Get nickname */
325       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
326       if (!tmp) {
327         silc_server_query_send_error(server, query,
328                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
329         silc_server_query_free(query);
330         return;
331       }
332
333       /* Get the nickname@server string and parse it */
334       if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
335         silc_server_query_send_error(server, query,
336                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
337         silc_server_query_free(query);
338         return;
339       }
340
341     } else {
342       /* Parse the IDs included in the query */
343       query->ids = silc_calloc(argc, sizeof(*query->ids));
344
345       for (i = 0; i < argc; i++) {
346         tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
347         if (!tmp)
348           continue;
349
350         id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
351         if (!id) {
352           silc_server_query_add_error(server, query, TRUE, i + 4,
353                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
354           continue;
355         }
356
357         query->ids[query->ids_count].id = id;
358         query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
359         query->ids_count++;
360       }
361     }
362
363     /* Get the max count of reply messages allowed */
364     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
365     if (tmp && tmp_len == sizeof(SilcUInt32))
366       SILC_GET32_MSB(query->reply_count, tmp);
367
368     /* Get requested attributes if set */
369     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
370     if (tmp)
371       query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
372     break;
373
374   case SILC_COMMAND_WHOWAS:
375     /* Get nickname */
376     tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
377     if (!tmp) {
378       silc_server_query_send_error(server, query,
379                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
380       silc_server_query_free(query);
381       return;
382     }
383
384     /* Get the nickname@server string and parse it */
385     if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
386       silc_server_query_send_error(server, query,
387                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
388       silc_server_query_free(query);
389       return;
390     }
391
392     /* Get the max count of reply messages allowed */
393     tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
394     if (tmp && tmp_len == sizeof(SilcUInt32))
395       SILC_GET32_MSB(query->reply_count, tmp);
396     break;
397
398   case SILC_COMMAND_IDENTIFY:
399     /* Get IDs if present. Take IDs always instead of names. */
400     tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
401     if (!tmp) {
402
403       /* Try get nickname */
404       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
405       if (tmp) {
406         /* Get the nickname@server string and parse it */
407         if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
408           silc_server_query_add_error(server, query, TRUE, 1,
409                                       SILC_STATUS_ERR_BAD_NICKNAME);
410       }
411
412       /* Try get server name */
413       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
414       if (tmp)
415         query->server_name = silc_memdup(tmp, tmp_len);
416
417       /* Get channel name */
418       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
419       if (tmp)
420         query->channel_name = silc_memdup(tmp, tmp_len);
421
422     } else {
423       /* Parse the IDs included in the query */
424       query->ids = silc_calloc(argc, sizeof(*query->ids));
425
426       for (i = 0; i < argc; i++) {
427         tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
428         if (!tmp)
429           continue;
430
431         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
432         if (!id) {
433           silc_server_query_add_error(server, query, TRUE, i + 5,
434                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
435           continue;
436         }
437
438         query->ids[query->ids_count].id = id;
439         query->ids[query->ids_count].id_type = id_type;
440         query->ids_count++;
441       }
442     }
443
444     /* Get the max count of reply messages allowed */
445     tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
446     if (tmp && tmp_len == sizeof(SilcUInt32))
447       SILC_GET32_MSB(query->reply_count, tmp);
448     break;
449   }
450
451   /* Start processing the query information */
452   silc_server_query_process(server, query);
453 }
454
455 /* Processes the parsed query.  This does the actual finding of the
456    queried information and prepares for sending reply to the original
457    sender of the query command.  It is guaranteed that this function
458    (which may be slow) is called only once for entire query. */
459
460 void silc_server_query_process(SilcServer server, SilcServerQuery query)
461 {
462   SilcServerCommandContext cmd = query->cmd;
463   bool check_global = FALSE;
464   void *entry;
465   SilcClientEntry *clients = NULL, client_entry;
466   SilcChannelEntry *channels = NULL;
467   SilcServerEntry *servers = NULL;
468   SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
469   int i;
470
471   /* Check global lists if query is coming from client or we are not
472      normal server (we know global information). */
473   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
474     check_global = TRUE;
475   else if (server->server_type != SILC_SERVER)
476     check_global = TRUE;
477
478   if (query->nickname) {
479     /* Get all clients matching nickname from local list */
480     if (!silc_idlist_get_clients_by_hash(server->local_list, 
481                                          query->nickname, server->md5hash,
482                                          &clients, &clients_count))
483       silc_idlist_get_clients_by_nickname(server->local_list, 
484                                           query->nickname,
485                                           query->nick_server,
486                                           &clients, &clients_count);
487
488     /* Check global list as well */
489     if (check_global) {
490       if (!silc_idlist_get_clients_by_hash(server->global_list, 
491                                            query->nickname, server->md5hash,
492                                            &clients, &clients_count))
493         silc_idlist_get_clients_by_nickname(server->global_list, 
494                                             query->nickname,
495                                             query->nick_server,
496                                             &clients, &clients_count);
497     }
498
499     if (!clients)
500       silc_server_query_add_error(server, query, TRUE, 1,
501                                   SILC_STATUS_ERR_NO_SUCH_NICK);
502   }
503
504   if (query->server_name) {
505     /* Find server by name */
506     entry = silc_idlist_find_server_by_name(server->local_list,
507                                             query->server_name, TRUE, NULL);
508     if (!entry && check_global)
509       entry = silc_idlist_find_server_by_name(server->global_list,
510                                               query->server_name, TRUE, NULL);
511     if (entry) {
512       servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
513       servers[servers_count++] = (SilcServerEntry)entry;
514     }
515
516     if (!servers)
517       silc_server_query_add_error(server, query, TRUE, 2,
518                                   SILC_STATUS_ERR_NO_SUCH_SERVER);
519   }
520
521   if (query->channel_name) {
522     /* Find channel by name */
523     entry = silc_idlist_find_channel_by_name(server->local_list,
524                                              query->channel_name, NULL);
525     if (!entry && check_global)
526       entry = silc_idlist_find_channel_by_name(server->global_list,
527                                                query->channel_name, NULL);
528     if (entry) {
529       channels = silc_realloc(channels, sizeof(*channels) *
530                               (channels_count + 1));
531       channels[channels_count++] = (SilcChannelEntry)entry;
532     }
533
534     if (!channels)
535       silc_server_query_add_error(server, query, TRUE, 3,
536                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL);
537   }
538
539   if (query->ids_count) {
540     /* Find entries by the queried IDs */
541     for (i = 0; i < query->ids_count; i++) {
542       void *id = query->ids[i].id;
543       if (!id)
544         continue;
545
546       switch (query->ids[i].id_type) {
547
548       case SILC_ID_CLIENT:
549         /* Get client entry */
550         entry = silc_idlist_find_client_by_id(server->local_list,
551                                               id, TRUE, NULL);
552         if (!entry && check_global)
553           entry = silc_idlist_find_client_by_id(server->global_list,
554                                                 id, TRUE, NULL);
555         if (!entry) {
556           silc_server_query_add_error(server, query, FALSE, i,
557                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
558           continue;
559         }
560
561         clients = silc_realloc(clients, sizeof(*clients) *
562                                (clients_count + 1));
563         clients[clients_count++] = (SilcClientEntry)entry;
564         break;
565
566       case SILC_ID_SERVER:
567         /* Get server entry */
568         entry = silc_idlist_find_server_by_id(server->local_list,
569                                               id, TRUE, NULL);
570         if (!entry && check_global)
571           entry = silc_idlist_find_server_by_id(server->global_list,
572                                                 id, TRUE, NULL);
573         if (!entry) {
574           silc_server_query_add_error(server, query, FALSE, i,
575                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
576           continue;
577         }
578
579         servers = silc_realloc(servers, sizeof(*servers) *
580                                (servers_count + 1));
581         servers[servers_count++] = (SilcServerEntry)entry;
582         break;
583
584       case SILC_ID_CHANNEL:
585         /* Get channel entry */
586         entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
587         if (!entry && check_global)
588           entry = silc_idlist_find_channel_by_id(server->global_list, id,
589                                                  NULL);
590         if (!entry) {
591           silc_server_query_add_error(server, query, FALSE, i,
592                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
593           continue;
594         }
595
596         channels = silc_realloc(channels, sizeof(*channels) *
597                                 (channels_count + 1));
598         channels[channels_count++] = (SilcChannelEntry)entry;
599         break;
600
601       default:
602         break;
603       }
604     }
605   }
606
607   /* If nothing was found, then just send the errors */
608   if (!clients && !channels && !servers) {
609     silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
610     silc_server_query_free(query);
611     return;
612   }
613
614   /* Now process all found information and if necessary do some more
615      querying. */
616   switch (query->querycmd) {
617
618   case SILC_COMMAND_WHOIS:
619     for (i = 0; i < clients_count; i++) {
620       client_entry = clients[i];
621
622       if (!client_entry)
623         continue;
624
625       /* If requested attributes is set then we always resolve the client
626          information, if not then check whether the entry is complete or not
627          and decide whether we need to resolve or not. */
628       if (!query->attrs) {
629         if ((client_entry->nickname && client_entry->username &&
630              client_entry->userinfo) ||
631             !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
632
633           /* Check if cannot query this anyway, so take next one */
634           if (!client_entry->router)
635             continue;
636
637           /* If we are router, client is local to us, or client is on channel
638              we do not need to resolve the client information. */
639           if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
640               || silc_hash_table_count(client_entry->channels))
641             continue;
642         }
643       }
644
645       /* When requested attributes is present and local client is detached
646          we cannot send the command to the client, we'll reply on behalf of
647          the client instead. */
648       if (query->attrs && SILC_IS_LOCAL(client_entry) &&
649           client_entry->mode & SILC_UMODE_DETACHED)
650         continue;
651
652       /* Resolve the detailed client information. If client is local we
653          know that attributes were present and we will resolve directly
654          from the client. Otherwise resolve from client's owner. */
655       silc_server_query_resolve(server, query,
656                                 (SILC_IS_LOCAL(client_entry) ?
657                                  client_entry->connection :
658                                  client_entry->router->connection),
659                                 client_entry);
660     }
661     break;
662
663   case SILC_COMMAND_WHOWAS:
664     for (i = 0; i < clients_count; i++) {
665       client_entry = clients[i];
666
667       /* Check if cannot query this anyway, so take next one */
668       if (!client_entry || !client_entry->router)
669         continue;
670
671       /* If both nickname and username are present no resolving is needed */
672       if (client_entry->nickname && client_entry->username)
673         continue;
674
675       /* Resolve the detailed client information */
676       silc_server_query_resolve(server, query,
677                                 client_entry->router->connection,
678                                 client_entry);
679     }
680     break;
681
682   case SILC_COMMAND_IDENTIFY:
683     for (i = 0; i < clients_count; i++) {
684       client_entry = clients[i];
685
686       /* Check if cannot query this anyway, so take next one */
687       if (!client_entry || !client_entry->router)
688         continue;
689
690       if (client_entry->nickname ||
691           !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
692         /* If we are router, client is local to us, or client is on channel
693            we do not need to resolve the client information. */
694         if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
695             || silc_hash_table_count(client_entry->channels))
696           continue;
697       }
698
699       /* Resolve the detailed client information */
700       silc_server_query_resolve(server, query,
701                                 client_entry->router->connection,
702                                 client_entry);
703     }
704     break;
705   }
706
707   if (!query->num_query)
708     /* If we didn't have to do any resolving, continue with sending the
709        command reply to the original sender. */
710     silc_server_query_send_reply(server, query, clients, clients_count,
711                                  servers, servers_count, channels,
712                                  channels_count);
713   else
714     /* Now actually send the resolvings we gathered earlier */
715     silc_server_query_resolve(server, query, NULL, NULL);
716
717   silc_free(clients);
718   silc_free(servers);
719   silc_free(channels);
720 }
721
722 /* Resolve the detailed information for the `client_entry'.  Only client
723    information needs to be resolved for being incomplete.  Each incomplete
724    client entry calls this function to do the resolving. */
725
726 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
727                                SilcSocketConnection sock,
728                                SilcClientEntry client_entry)
729 {
730 #if 0
731   SilcUInt16 ident;
732
733   if (!sock && client_entry)
734     return;
735
736   /* If arguments are NULL we will now actually send the resolvings
737      that earlier has been gathered by calling this function. */
738   if (!sock && !client_entry) {
739
740     return;
741   }
742
743   if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
744     /* The entry is being resolved by some other external query already.
745        Attach to that query instead of resolving again. */
746     ident = client_entry->resolve_cmd_ident;
747     silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
748                                 silc_server_query_resolve_reply, query);
749   } else {
750     ident = ++server->cmd_ident;
751
752     switch (query->querycmd) {
753
754     case SILC_COMMAND_WHOIS:
755     case SILC_COMMAND_IDENTIFY:
756       break;
757
758     case SILC_COMMAND_WHOWAS:
759       /* We must send WHOWAS command since it's the only the way of
760          resolving clients that are not present in the network anymore. */
761       silc_server_send_command(server, sock, query->querycmd, ident, 1,
762                                1, query->nickname, strlen(query->nickname));
763       break;
764     }
765
766     silc_server_command_pending(server, query->querycmd, ident,
767                                 silc_server_query_resolve_reply, query);
768   }
769
770   /* Mark the entry as being resolved */
771   client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
772   client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
773   client_entry->resovle_cmd_ident = ident;
774
775   /* Save the query information, which we will reprocess after we
776      get this and all other queries back. */
777   query->ids = silc_realloc(query->ids, sizeof(*query->ids) *
778                             (query->ids_count + 1));
779   if (query->ids) {
780     query->ids[query->ids_count].id = silc_id_dup(id, id_type);
781     query->ids[query->ids_count].id_type = id_type;
782     query->ids[query->ids_count].ident = ident;
783     query->ids_count++;
784   }
785   query->num_query++;
786 #endif
787 }
788
789 /* Reply callback called after one resolving has been completed.  If
790    all resolvings has been received then we will continue with sending
791    the command reply to the original sender of the query. */
792
793 void silc_server_query_resolve_reply(void *context, void *reply)
794 {
795   SilcServerQuery query = context;
796
797
798 }
799
800 void silc_server_query_send_reply(SilcServer server,
801                                   SilcServerQuery query,
802                                   SilcClientEntry *clients,
803                                   SilcUInt32 clients_count,
804                                   SilcServerEntry *servers,
805                                   SilcUInt32 servers_count,
806                                   SilcChannelEntry *channels,
807                                   SilcUInt32 channels_count)
808 {
809
810 }
811
812 /* Find client by the Client ID indicated by the `client_id', and if not
813    found then query it by using WHOIS command.  The client information
814    is also resolved if the cached information is incomplete or if the
815    `always_resolve' is set to TRUE.  The indication whether requested
816    client was being resolved is saved into `resolved'.  If the client
817    is not being resolved its entry is returned by this function.  NULL
818    is returned if client is resolved. */
819
820 SilcClientEntry silc_server_query_client(SilcServer server,
821                                          const SilcClientID *client_id,
822                                          bool always_resolve,
823                                          bool *resolved)
824 {
825   SilcClientEntry client;
826
827   if (resolved)
828     *resolved = FALSE;
829
830   client = silc_idlist_find_client_by_id(server->local_list,
831                                          (SilcClientID *)client_id,
832                                          TRUE, NULL);
833   if (!client) {
834     client = silc_idlist_find_client_by_id(server->global_list,
835                                            (SilcClientID *)client_id,
836                                            TRUE, NULL);
837     if (!client && server->server_type == SILC_ROUTER)
838       return NULL;
839   }
840
841   if (!client && server->standalone)
842     return NULL;
843
844   if (!client || !client->nickname || !client->username ||
845       always_resolve) {
846     SilcBuffer buffer, idp;
847
848     if (client) {
849       client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
850       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
851       client->resolve_cmd_ident = ++server->cmd_ident;
852     }
853
854     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
855     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
856                                             server->cmd_ident, 1,
857                                             4, idp->data, idp->len);
858     silc_server_packet_send(server, client ? client->router->connection :
859                             SILC_PRIMARY_ROUTE(server),
860                             SILC_PACKET_COMMAND, 0,
861                             buffer->data, buffer->len, FALSE);
862     silc_buffer_free(idp);
863     silc_buffer_free(buffer);
864
865     if (resolved)
866       *resolved = TRUE;
867
868     return NULL;
869   }
870
871   return client;
872 }