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