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