Fixed memory leaks. In TIMEDOUT error remove the RESOLVING
[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           silc_strncat(uh, sizeof(uh), entry->username,
1211                        strlen(entry->username));
1212           if (!strchr(entry->username, '@') && entry->connection) {
1213             hsock = entry->connection;
1214             silc_strncat(uh, sizeof(uh), "@", 1);
1215             len = strlen(hsock->hostname);
1216             silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1217           }
1218
1219           if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
1220             channels =
1221               silc_server_get_client_channel_list(server, entry, FALSE, 
1222                                                   FALSE, &umode_list);
1223           else
1224             channels =
1225               silc_server_get_client_channel_list(server, entry, TRUE, 
1226                                                   TRUE, &umode_list);
1227
1228           if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
1229             fingerprint = entry->data.fingerprint;
1230           else
1231             fingerprint = NULL;
1232
1233           SILC_PUT32_MSB(entry->mode, mode);
1234           if (entry->connection)
1235             SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
1236
1237           /* If Requested Attribute were present, and we do not have the
1238              attributes we will reply to them on behalf of the client. */
1239           len = 0;
1240           if (query->attrs) {
1241             if (!entry->attrs) {
1242               attrs = silc_server_query_reply_attrs(server, query, &len);
1243             } else {
1244               attrs = entry->attrs;
1245               len = entry->attrs_len;
1246             }
1247           }
1248
1249           /* Send command reply */
1250           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1251                                          status, 0, ident, 10,
1252                                          2, idp->data, idp->len,
1253                                          3, nh, strlen(nh),
1254                                          4, uh, strlen(uh),
1255                                          5, entry->userinfo, 
1256                                          strlen(entry->userinfo),
1257                                          6, channels ? channels->data : NULL,
1258                                          channels ? channels->len : 0,
1259                                          7, mode, 4,
1260                                          8, idle, 4,
1261                                          9, fingerprint,
1262                                          fingerprint ? 20 : 0,
1263                                          10, umode_list ? umode_list->data :
1264                                          NULL, umode_list ? umode_list->len :
1265                                          0, 11, attrs, len);
1266
1267           sent_reply = TRUE;
1268
1269           /* For now we will delete Requested Attributes */
1270           silc_free(entry->attrs);
1271           entry->attrs = NULL;
1272
1273           if (channels)
1274             silc_buffer_free(channels);
1275           if (umode_list) {
1276             silc_buffer_free(umode_list);
1277             umode_list = NULL;
1278           }
1279         }
1280         break;
1281
1282       case SILC_COMMAND_IDENTIFY:
1283         if (!entry->username) {
1284           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1285                                          status, 0, ident, 2,
1286                                          2, idp->data, idp->len,
1287                                          3, nh, strlen(nh));
1288           sent_reply = TRUE;
1289         } else {
1290           silc_strncat(uh, sizeof(uh), entry->username,
1291                        strlen(entry->username));
1292           if (!strchr(entry->username, '@') && entry->connection) {
1293             hsock = entry->connection;
1294             silc_strncat(uh, sizeof(uh), "@", 1);
1295             len = strlen(hsock->hostname);
1296             silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1297           }
1298
1299           silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1300                                          status, 0, ident, 3,
1301                                          2, idp->data, idp->len,
1302                                          3, nh, strlen(nh),
1303                                          4, uh, strlen(uh));
1304           sent_reply = TRUE;
1305         }
1306         break;
1307
1308       case SILC_COMMAND_WHOWAS:
1309         silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
1310         if (!strchr(entry->username, '@'))
1311           silc_strncat(uh, sizeof(uh), "@*private*", 10);
1312
1313         /* Send command reply */
1314         silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1315                                        status, 0, ident, 4,
1316                                        2, idp->data, idp->len,
1317                                        3, nh, strlen(nh),
1318                                        4, uh, strlen(uh),
1319                                        5, entry->userinfo, 
1320                                        entry->userinfo ? 
1321                                        strlen(entry->userinfo) : 0);
1322         sent_reply = TRUE;
1323         break;
1324       }
1325
1326       silc_buffer_free(idp);
1327
1328       if (status == SILC_STATUS_LIST_END)
1329         break;
1330       k++;
1331     }
1332
1333     if (k == 0) {
1334       /* Not one valid entry was found, send error.  If nickname was used
1335          in query send error based on that, otherwise the query->errors
1336          already includes proper errors. */
1337       if (query->nickname)
1338         silc_server_query_add_error(server, query, TRUE, 1,
1339                                     SILC_STATUS_ERR_NO_SUCH_NICK);
1340     }
1341   }
1342
1343   /* Send servers */
1344   if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
1345     SilcServerEntry entry;
1346
1347     if (status == SILC_STATUS_OK && servers_count > 1)
1348       status = SILC_STATUS_LIST_START;
1349
1350     k = 0;
1351     for (i = 0; i < servers_count; i++) {
1352       entry = servers[i];
1353       
1354       if (k >= 1)
1355         status = SILC_STATUS_LIST_ITEM;
1356       if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
1357           !query->errors_count)
1358         status = SILC_STATUS_LIST_END;
1359       if (query->reply_count && k - 1 == query->reply_count)
1360         status = SILC_STATUS_LIST_END;
1361       
1362       SILC_LOG_DEBUG(("%s: server %s",
1363                       (status == SILC_STATUS_OK ?         "   OK" :
1364                        status == SILC_STATUS_LIST_START ? "START" :
1365                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1366                        status == SILC_STATUS_LIST_END  ?  "  END" :
1367                        "      : "),
1368                       entry->server_name ? entry->server_name : ""));
1369
1370       /* Send command reply */
1371       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
1372       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1373                                      status, 0, ident, 2,
1374                                      2, idp->data, idp->len, 
1375                                      3, entry->server_name, 
1376                                      entry->server_name ? 
1377                                      strlen(entry->server_name) : 0);
1378       silc_buffer_free(idp);
1379       sent_reply = TRUE;
1380       
1381       if (status == SILC_STATUS_LIST_END)
1382         break;
1383       k++;
1384     }
1385   }
1386
1387   /* Send channels */
1388   if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
1389     SilcChannelEntry entry;
1390
1391     if (status == SILC_STATUS_OK && channels_count > 1)
1392       status = SILC_STATUS_LIST_START;
1393
1394     k = 0;
1395     for (i = 0; i < channels_count; i++) {
1396       entry = channels[i];
1397       
1398       if (k >= 1)
1399         status = SILC_STATUS_LIST_ITEM;
1400       if (channels_count > 1 && k == channels_count - 1 &&
1401           !query->errors_count)
1402         status = SILC_STATUS_LIST_END;
1403       if (query->reply_count && k - 1 == query->reply_count)
1404         status = SILC_STATUS_LIST_END;
1405       
1406       SILC_LOG_DEBUG(("%s: channel %s",
1407                       (status == SILC_STATUS_OK ?         "   OK" :
1408                        status == SILC_STATUS_LIST_START ? "START" :
1409                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1410                        status == SILC_STATUS_LIST_END  ?  "  END" :
1411                        "      : "),
1412                       entry->channel_name ? entry->channel_name : ""));
1413
1414       /* Send command reply */
1415       idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
1416       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1417                                      status, 0, ident, 2,
1418                                      2, idp->data, idp->len, 
1419                                      3, entry->channel_name, 
1420                                      entry->channel_name ? 
1421                                      strlen(entry->channel_name) : 0);
1422       silc_buffer_free(idp);
1423       sent_reply = TRUE;
1424       
1425       if (status == SILC_STATUS_LIST_END)
1426         break;
1427       k++;
1428     }
1429   }
1430
1431   /* Send errors */
1432   if (query->errors_count) {
1433     int type;
1434
1435     if (status == SILC_STATUS_OK && query->errors_count > 1)
1436       status = SILC_STATUS_LIST_START;
1437
1438     k = 0;
1439     for (i = 0; i < query->errors_count; i++) {
1440       idp = NULL;
1441
1442       /* Take error argument */
1443       if (query->errors[i].from_cmd) {
1444         len = 0;
1445         tmp = silc_argument_get_arg_type(cmd->args,
1446                                          query->errors[i].index, &len);
1447         if (query->errors[i].index == 1)
1448           type = 3;                 /* Nickname */
1449         else
1450           type = 2;                 /* ID */
1451       } else if (!query->errors[i].id) {
1452         idp =
1453           silc_id_payload_encode(query->ids[query->errors[i].index].id,
1454                                  query->ids[query->errors[k].index].id_type);
1455         tmp = idp->data;
1456         len = idp->len;
1457         type = 2;
1458       } else {
1459         idp = silc_id_payload_encode(query->errors[i].id,
1460                                      query->errors[k].id_type);
1461         tmp = idp->data;
1462         len = idp->len;
1463         type = 2;
1464       }
1465
1466       if (k >= 1)
1467         status = SILC_STATUS_LIST_ITEM;
1468       if (query->errors_count > 1 && k == query->errors_count - 1)
1469         status = SILC_STATUS_LIST_END;
1470       if (query->reply_count && k - 1 == query->reply_count)
1471         status = SILC_STATUS_LIST_END;
1472
1473       SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
1474                       (status == SILC_STATUS_OK ?         "   OK" :
1475                        status == SILC_STATUS_LIST_START ? "START" :
1476                        status == SILC_STATUS_LIST_ITEM  ? " ITEM" :
1477                        status == SILC_STATUS_LIST_END  ?  "  END" :
1478                        "      : "), 
1479                       silc_get_status_message(query->errors[i].error),
1480                       query->errors[i].error));
1481
1482       /* Send error */
1483       silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1484                                      (status == SILC_STATUS_OK ?
1485                                       query->errors[i].error : status),
1486                                      (status == SILC_STATUS_OK ?
1487                                       0 : query->errors[i].error), ident, 1,
1488                                      type, tmp, len);
1489       silc_buffer_free(idp);
1490       sent_reply = TRUE;
1491
1492       if (status == SILC_STATUS_LIST_END)
1493         break;
1494       k++;
1495     }
1496   }
1497
1498   if (!sent_reply)
1499     SILC_LOG_ERROR(("BUG: Query did not send anything"));
1500
1501   /* Cleanup */
1502   silc_server_query_free(query);
1503 }
1504
1505 /* This routine is used to reply to Requested Attributes in WHOIS on behalf
1506    of the client since we were unable to resolve them from the client.
1507    Either client does not support Requested Attributes or isn't replying
1508    to them like it should. */
1509
1510 unsigned char *silc_server_query_reply_attrs(SilcServer server,
1511                                              SilcServerQuery query,
1512                                              SilcUInt32 *attrs_len)
1513 {
1514   unsigned char *attrs = NULL;
1515   SilcUInt32 len = 0;
1516
1517   SILC_LOG_DEBUG(("Constructing Requested Attributes"));
1518
1519   if (attrs_len)
1520     *attrs_len = len;
1521
1522   return attrs;
1523 }
1524
1525 /* Find client by the Client ID indicated by the `client_id', and if not
1526    found then query it by using WHOIS command.  The client information
1527    is also resolved if the cached information is incomplete or if the
1528    `always_resolve' is set to TRUE.  The indication whether requested
1529    client was being resolved is saved into `resolved'.  If the client
1530    is not being resolved its entry is returned by this function.  NULL
1531    is returned if client is resolved. */
1532
1533 SilcClientEntry silc_server_query_client(SilcServer server,
1534                                          const SilcClientID *client_id,
1535                                          bool always_resolve,
1536                                          bool *resolved)
1537 {
1538   SilcClientEntry client;
1539
1540   SILC_LOG_DEBUG(("Resolving client by client ID"));
1541
1542   if (resolved)
1543     *resolved = FALSE;
1544
1545   client = silc_idlist_find_client_by_id(server->local_list,
1546                                          (SilcClientID *)client_id,
1547                                          TRUE, NULL);
1548   if (!client) {
1549     client = silc_idlist_find_client_by_id(server->global_list,
1550                                            (SilcClientID *)client_id,
1551                                            TRUE, NULL);
1552     if (!client && server->server_type == SILC_ROUTER)
1553       return NULL;
1554   }
1555
1556   if (!client && server->standalone)
1557     return NULL;
1558
1559   if (!client || !client->nickname || !client->username ||
1560       always_resolve) {
1561     SilcBuffer buffer, idp;
1562
1563     if (client) {
1564       client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
1565       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
1566       client->resolve_cmd_ident = ++server->cmd_ident;
1567     }
1568
1569     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
1570     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
1571                                             server->cmd_ident, 1,
1572                                             4, idp->data, idp->len);
1573     silc_server_packet_send(server, client ? client->router->connection :
1574                             SILC_PRIMARY_ROUTE(server),
1575                             SILC_PACKET_COMMAND, 0,
1576                             buffer->data, buffer->len, FALSE);
1577     silc_buffer_free(idp);
1578     silc_buffer_free(buffer);
1579
1580     if (resolved)
1581       *resolved = TRUE;
1582
1583     return NULL;
1584   }
1585
1586   return client;
1587 }