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