Added SILC Thread Queue API
[crypto.git] / lib / silcserver / server_st_query.c
1 /*
2
3   server_st_query.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2006 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
20 #include "silc.h"
21 #include "silcserver.h"
22 #include "server_internal.h"
23
24 /************************** Types and definitions ***************************/
25
26 /* Resolving entry */
27 typedef struct SilcServerQueryResolveStruct {
28   struct SilcServerQueryResolveStruct *next;
29   SilcFSMThreadStruct thread;       /* FSM thread for waiting reply */
30   SilcServerPending pending;        /* Pending command context */
31   SilcPacketStream stream;          /* Resolving connection */
32   SilcID *ids;                      /* Resolved IDs */
33   unsigned int ids_count     : 30;  /* Number of resolved IDs */
34   unsigned int attached      :  1;  /* Set if attached to a resolving */
35   unsigned int local         :  1;  /* Set if client is local to us */
36 } *SilcServerQueryResolve;
37
38 /* Represents one error occurred during query */
39 typedef struct {
40   SilcID id;                        /* ID */
41   unsigned int index : 15;          /* Index to IDs */
42   unsigned int type : 2;            /* 0 = take from query->ids, 1 = take
43                                        from args, 2 = no args in error. */
44   unsigned int error : 7;           /* The actual error (SilcStatus) */
45 } *SilcServerQueryError;
46
47 /* Query session context */
48 typedef struct {
49   /* Queried data */
50   char *nickname;                   /* Queried nickname, normalized */
51   char *nick_server;                /* Queried nickname's server */
52   char *server_name;                /* Queried server name, normalized */
53   char *channel_name;               /* Queried channel name, normalized */
54   SilcID *ids;                      /* Queried IDs */
55   SilcUInt32 ids_count;             /* number of queried IDs */
56   SilcUInt32 reply_count;           /* Requested reply count */
57   SilcDList attrs;                  /* Requested Attributes in WHOIS */
58   SilcFSMEventStruct wait_resolve;   /* Resolving signaller */
59
60   /* Query session data */
61   SilcServerComman cmd;             /* Command context for query */
62   SilcList clients;                 /* Found clients */
63   SilcList servers;                 /* Found servers */
64   SilcList channels;                /* Found channels */
65   SilcList resolve;                 /* Clients to resolve */
66   SilcList resolvings;              /* Ongoing resolvings */
67   SilcServerQueryError errors;      /* Query errors */
68   SilcServerPending redirect;       /* Pending redirect */
69   SilcUInt16 errors_count;          /* number of errors */
70   SilcUInt8 resolve_retry;          /* Resolving retry count */
71   SilcCommand querycmd;             /* Query command */
72 } *SilcServerQuery;
73
74
75 /************************ Static utility functions **************************/
76
77
78 /********************************* WHOIS ************************************/
79
80 SILC_FSM_STATE(silc_server_st_query_whois)
81 {
82   SilcServerThread thread = fsm_context;
83   SilcServer server = thread->server;
84   SilcServerCommand cmd = state_context;
85   SilcArgumentPayload args = silc_command_get_args(cmd->payload);
86   SilcServerQuery query;
87
88   SILC_LOG_DEBUG(("WHOIS query"));
89
90   query = silc_calloc(1, sizeof(*query));
91   if (!query) {
92     silc_server_command_free(cmd);
93     return SILC_FSM_FINISH;
94   }
95
96   query->querycmd = SILC_COMMAND_WHOIS;
97   query->cmd = cmd;
98
99   silc_fsm_set_state_context(fsm, query);
100
101   /* If we are normal server and query contains a nickname OR query
102      doesn't contain nickname or ids BUT does contain user attributes,
103      send it to the router */
104   if (server->server_type != SILC_ROUTER && !server->standalone &&
105       cmd->packet->stream != SILC_PRIMARY_ROUTE(server) &&
106       (silc_argument_get_arg_type(args, 1, NULL) ||
107        (!silc_argument_get_arg_type(args, 1, NULL) &&
108         !silc_argument_get_arg_type(args, 4, NULL) &&
109         silc_argument_get_arg_type(args, 3, NULL)))) {
110     /** Send query to router */
111     silc_fsm_next(fsm, silc_server_st_query_send_router);
112     return SILC_FSM_CONTINUE;
113   }
114
115   /** Parse WHOIS query */
116   silc_fsm_next(fsm, silc_server_st_query_parse);
117   return SILC_FSM_CONTINUE;
118 }
119
120
121 /********************************* WHOWAS ***********************************/
122
123 SILC_FSM_STATE(silc_server_st_query_whowas)
124 {
125   SilcServerThread thread = fsm_context;
126   SilcServerCommand cmd = state_context;
127
128   SILC_LOG_DEBUG(("WHOWAS query"));
129
130   query = silc_calloc(1, sizeof(*query));
131   if (!query) {
132     silc_server_command_free(cmd);
133     return SILC_FSM_FINISH;
134   }
135
136   query->querycmd = SILC_COMMAND_WHOWAS;
137   query->cmd = cmd;
138
139   silc_fsm_set_state_context(fsm, query);
140
141   /* WHOWAS query is always sent to router if we are normal server */
142   if (server->server_type == SILC_SERVER && !server->standalone &&
143       cmd->packet->stream != SILC_PRIMARY_ROUTE(server)) {
144     /** Send query to router */
145     silc_fsm_next(fsm, silc_server_st_query_send_router);
146     return SILC_FSM_CONTINUE;
147   }
148
149   /** Parse WHOWAS query */
150   silc_fsm_next(fsm, silc_server_st_query_parse);
151   return SILC_FSM_CONTINUE;
152 }
153
154
155 /******************************** IDENTIFY **********************************/
156
157 SILC_FSM_STATE(silc_server_st_query_identify)
158 {
159   SilcServerThread thread = fsm_context;
160   SilcServerCommand cmd = state_context;
161   SilcArgumentPayload args = silc_command_get_args(cmd->payload);
162
163   SILC_LOG_DEBUG(("IDENTIFY query"));
164
165   query = silc_calloc(1, sizeof(*query));
166   if (!query) {
167     silc_server_command_free(cmd);
168     return SILC_FSM_FINISH;
169   }
170
171   query->querycmd = SILC_COMMAND_IDENTIFY;
172   query->cmd = cmd;
173
174   silc_fsm_set_state_context(fsm, query);
175
176   /* If we are normal server and query does not contain IDs, send it directly
177      to router (it contains nickname, server name or channel name). */
178   if (server->server_type == SILC_SERVER && !server->standalone &&
179       cmd->packet->stream != SILC_PRIMARY_ROUTE(server) &&
180       !silc_argument_get_arg_type(args, 5, NULL)) {
181     /** Send query to router */
182     silc_fsm_next(fsm, silc_server_st_query_send_router);
183     return SILC_FSM_CONTINUE;
184   }
185
186   /** Parse IDENTIFY query */
187   silc_fsm_next(fsm, silc_server_st_query_parse);
188   return SILC_FSM_CONTINUE;
189 }
190
191
192 /**************************** Query redirecting *****************************/
193
194 /* Send the query to router for further processing */
195
196 SILC_FSM_STATE(silc_server_st_query_send_router)
197 {
198   SilcServerThread thread = fsm_context;
199   SilcServer server = thread->server;
200   SilcServerQuery query = state_context;
201   SilcBuffer tmpbuf;
202   SilcUInt16 cmd_ident, old_ident;
203
204   SILC_LOG_DEBUG(("Redirecting query to router"));
205
206   /* Send the command to our router */
207   cmd_ident = silc_server_cmd_ident(server);
208   old_ident = silc_command_get_ident(query->cmd->payload);
209   silc_command_set_ident(query->cmd->payload, cmd_ident);
210
211   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
212   if (!tmpbuf || !silc_packet_send(SILC_PRIMARY_ROUTE(server),
213                                    SILC_PACKET_COMMAND, 0,
214                                    tmpbuf->data, silc_buffer_len(tmpbuf))) {
215     /** Error sending packet */
216     silc_server_query_send_error(server, query,
217                                  SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
218     silc_fsm_next(fsm, silc_server_st_query_error);
219     return SILC_FSM_CONTINUE;
220   }
221
222   silc_command_set_ident(query->cmd->payload, old_ident);
223   silc_buffer_free(tmpbuf);
224
225   /* Statistics */
226   server->stat.commands_sent++;
227
228   /* Continue parsing the query after receiving reply from router */
229   query->redirect = silc_server_command_pending(thread, query->redirect_ident);
230   if (!query->redirect) {
231     /** No memory */
232     silc_server_query_send_error(server, query,
233                                  SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
234     silc_fsm_next(fsm, silc_server_st_query_error);
235     return SILC_FSM_CONTINUE;
236   }
237
238   /** Wait router reply */
239   query->resolved = TRUE;
240   silc_fsm_next(fsm, silc_server_st_query_router_reply)
241   return SILC_FSM_CONTINUE;
242 }
243
244 /* Wait for router reply and process the reply when it arrives. */
245
246 SILC_FSM_STATE(silc_server_st_query_router_reply)
247 {
248   SilcServerThread thread = fsm_context;
249   SilcServer server = thread->server;
250   SilcServerQuery query = state_context;
251   SilcServerPending pending = query->redirect;
252   SilcBool timedout;
253
254   /* Wait here for the reply */
255   SILC_FSM_EVENT_TIMEDWAIT(&pending->wait_reply, 10, 0, &timedout);
256
257   if (timedout) {
258     /** Timeout waiting reply */
259     silc_server_command_pending_free(thread, pending);
260     silc_server_query_send_error(server, query, SILC_STATUS_ERR_TIMEDOUT, 0);
261     silc_fsm_next(fsm, silc_server_st_query_error);
262     return SILC_FSM_CONTINUE;
263   }
264
265   /* Check if the query failed */
266   if (!silc_command_get_status(pending->reply->payload, NULL, NULL)) {
267     SilcBuffer buffer;
268
269     SILC_LOG_DEBUG(("Sending error to original query"));
270
271     /* Send the same command reply payload which contains the error */
272     silc_command_set_command(pending->reply->payload, query->querycmd);
273     silc_command_set_ident(pending->reply->payload,
274                            silc_command_get_ident(query->cmd->payload));
275     buffer = silc_command_payload_encode_payload(pending->reply->payload);
276     if (buffer)
277       silc_packet_send(query->cmd->packet->stream,
278                        SILC_PACKET_COMMAND_REPLY, 0,
279                        buffer->data, silc_buffer_len(buffer));
280     silc_buffer_free(buffer);
281
282     /* Statistics */
283     server->stat.commands_sent++;
284
285     /** Query error received */
286     silc_server_command_pending_free(thread, pending);
287     silc_fsm_next(fsm, silc_server_st_query_error);
288     return SILC_FSM_CONTINUE;
289   }
290
291   silc_server_command_pending_free(thread, pending);
292
293   /** Parse query command */
294   silc_fsm_next(fsm, silc_server_st_query_parse);
295   return SILC_FSM_CONTINUE;
296 }
297
298 /***************************** Query processing *****************************/
299
300 /* Parse the command query */
301
302 SILC_FSM_STATE(silc_server_st_query_parse)
303 {
304   SilcServerThread thread = fsm_context;
305   SilcServerQuery query = state_context;
306   SilcServerCommand cmd = query->cmd;
307   SilcArgumentPayload args = silc_command_get_args(cmd->payload);
308   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(args);
309   unsigned char *tmp;
310   SilcID id;
311   int i;
312
313   SILC_LOG_DEBUG(("Parsing %s query",
314                   silc_get_command_name(query->querycmd)));
315
316   switch (query->querycmd) {
317
318   case SILC_COMMAND_WHOIS:
319     /* Get requested attributes if set */
320     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
321     if (tmp && !query->attrs && tmp_len <= SILC_ATTRIBUTE_MAX_REQUEST_LEN)
322       query->attrs = silc_attribute_payload_parse(tmp, tmp_len);
323
324     /* Get Client IDs if present. Take IDs always instead of nickname. */
325     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
326     if (!tmp) {
327       /* No IDs present */
328
329       /* Get nickname */
330       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
331       if (!tmp && !query->attrs) {
332         /* No nickname, no ids and no attributes - send error */
333         silc_server_query_send_error(server, query,
334                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
335
336         /** Not enough arguments */
337         silc_fsm_next(fsm, silc_server_st_query_error);
338         return SILC_FSM_CONTINUE;
339       }
340
341       /* Get the nickname@server string and parse it */
342       if (tmp && ((tmp_len > 128) ||
343                   !silc_parse_userfqdn(tmp, &query->nickname,
344                                        &query->nick_server))) {
345         /** Bad nickname */
346         silc_server_query_send_error(server, query,
347                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
348         silc_fsm_next(fsm, silc_server_st_query_error);
349         return SILC_FSM_CONTINUE;
350       }
351
352       /* Check nickname */
353       if (tmp) {
354         tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
355                                     SILC_STRING_UTF8, 128, &tmp_len);
356         if (!tmp) {
357           /** Bad nickname */
358           silc_server_query_send_error(server, query,
359                                        SILC_STATUS_ERR_BAD_NICKNAME, 0);
360           silc_fsm_next(fsm, silc_server_st_query_error);
361           return SILC_FSM_CONTINUE;
362         }
363         /* XXX why free nickname */
364         silc_free(query->nickname);
365         query->nickname = tmp;
366       }
367
368     } else {
369       /* Parse the IDs included in the query */
370       query->ids = silc_calloc(argc - 3, sizeof(*query->ids));
371       if (!query->ids) {
372         /** No memory */
373         silc_server_query_send_error(server, query,
374                                      SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
375         silc_fsm_next(fsm, silc_server_st_query_error);
376         return SILC_FSM_CONTINUE;
377       }
378
379       for (i = 0; i < argc - 3; i++) {
380         tmp = silc_argument_get_arg_type(args, i + 4, &tmp_len);
381         if (!tmp)
382           continue;
383
384         if (!silc_id_payload_parse_id(tmp, tmp_len, &id) ||
385             id.type != SILC_ID_CLIENT) {
386           silc_server_query_add_error(server, query, 1, i + 4,
387                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
388           continue;
389         }
390
391         /* Normal server must check whether this ID exist, and if not then
392            send the query to router, unless done so already */
393         if (server->server_type == SILC_SERVER && !query->resolved &&
394             !silc_server_find_client_by_id(server, &client_id, TRUE, NULL)) {
395           /** Send query to router */
396           silc_free(query->ids);
397           query->ids = NULL;
398           query->ids_count = 0;
399           silc_fsm_next(fsm, silc_server_st_query_send_router);
400           return SILC_FSM_CONTINUE;
401         }
402
403         query->ids[query->ids_count] = id;
404         query->ids_count++;
405       }
406     }
407
408     /* Get the max count of reply messages allowed */
409     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
410     if (tmp && tmp_len == sizeof(SilcUInt32))
411       SILC_GET32_MSB(query->reply_count, tmp);
412     break
413
414   case SILC_COMMAND_WHOWAS:
415     /* Get nickname */
416     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
417     if (!tmp) {
418       /** Not enough arguments */
419       silc_server_query_send_error(server, query,
420                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
421       silc_fsm_next(fsm, silc_server_st_query_error);
422       return SILC_FSM_CONTINUE;
423     }
424
425     /* Get the nickname@server string and parse it */
426     if (tmp_len > 128 ||
427         !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
428       /** Bad nickname */
429       silc_server_query_send_error(server, query,
430                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
431       silc_fsm_next(fsm, silc_server_st_query_error);
432       return SILC_FSM_CONTINUE;
433     }
434
435     /* Check nickname */
436     tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
437                                 SILC_STRING_UTF8, 128, &tmp_len);
438     if (!tmp) {
439       /** Bad nickname */
440       silc_server_query_send_error(server, query,
441                                    SILC_STATUS_ERR_BAD_NICKNAME, 0);
442       silc_fsm_next(fsm, silc_server_st_query_error);
443       return SILC_FSM_CONTINUE;
444     }
445     /* XXX why free nickname */
446     silc_free(query->nickname);
447     query->nickname = tmp;
448
449     /* Get the max count of reply messages allowed */
450     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
451     if (tmp && tmp_len == sizeof(SilcUInt32))
452       SILC_GET32_MSB(query->reply_count, tmp);
453     break;
454
455   case SILC_COMMAND_IDENTIFY:
456     /* Get IDs if present. Take IDs always instead of names. */
457     tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
458     if (!tmp) {
459       /* No IDs present */
460
461       /* Try get nickname */
462       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
463       if (tmp) {
464         /* Get the nickname@server string and parse it */
465         if (tmp_len > 128 ||
466             !silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
467           silc_server_query_add_error(server, query, 1, 1,
468                                       SILC_STATUS_ERR_BAD_NICKNAME);
469
470         /* Check nickname */
471         tmp = silc_identifier_check(query->nickname, strlen(query->nickname),
472                                     SILC_STRING_UTF8, 128, &tmp_len);
473         if (!tmp) {
474           /** Bad nickname */
475           silc_server_query_send_error(server, query,
476                                        SILC_STATUS_ERR_BAD_NICKNAME, 0);
477           silc_fsm_next(fsm, silc_server_st_query_error);
478           return SILC_FSM_CONTINUE;
479         }
480         /* XXX why free nickname */
481         silc_free(query->nickname);
482         query->nickname = tmp;
483       }
484
485       /* Try get server name */
486       tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
487       if (tmp) {
488         /* Check server name */
489         tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
490                                     256, &tmp_len);
491         if (!tmp) {
492           /** Bad server name */
493           silc_server_query_send_error(server, query,
494                                        SILC_STATUS_ERR_BAD_SERVER, 0);
495           silc_fsm_next(fsm, silc_server_st_query_error);
496           return SILC_FSM_CONTINUE;
497         }
498         query->server_name = tmp;
499       }
500
501       /* Get channel name */
502       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
503       if (tmp && tmp_len <= 256) {
504         /* Check channel name */
505         tmp = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8,
506                                     256, &tmp_len);
507         if (!tmp) {
508           /** Bad channel name */
509           silc_server_query_send_error(server, query,
510                                        SILC_STATUS_ERR_BAD_CHANNEL, 0);
511           silc_fsm_next(fsm, silc_server_st_query_error);
512           return SILC_FSM_CONTINUE;
513         }
514         query->channel_name = tmp;
515       }
516
517       if (!query->nickname && !query->server_name && !query->channel_name) {
518         /** Nothing was queried */
519         silc_server_query_send_error(server, query,
520                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
521         silc_fsm_next(fsm, silc_server_st_query_error);
522         return SILC_FSM_CONTINUE;
523       }
524
525     } else {
526       /* Parse the IDs included in the query */
527       query->ids = silc_calloc(argc - 4, sizeof(*query->ids));
528
529       for (i = 0; i < argc - 4; i++) {
530         tmp = silc_argument_get_arg_type(args, i + 5, &tmp_len);
531         if (!tmp)
532           continue;
533
534         if (!silc_id_payload_parse_id(tmp, tmp_len, &id)) {
535           silc_server_query_add_error(server, query, 1, i + 5,
536                                       SILC_STATUS_ERR_BAD_CLIENT_ID);
537           continue;
538         }
539
540         /* Normal server must check whether this ID exist, and if not then
541            send the query to router, unless done so already */
542         if (server->server_type == SILC_SERVER && !query->resolved) {
543           if (id.type == SILC_ID_CLIENT) {
544             if (!silc_server_find_client_by_id(server, id, TRUE, NULL)) {
545               /** Send query to router */
546               silc_free(query->ids);
547               query->ids = NULL;
548               query->ids_count = 0;
549               silc_fsm_next(fsm, silc_server_st_query_send_router);
550               return SILC_FSM_CONTINUE;
551             }
552           } else {
553             /* For now all other ID's except Client ID's are explicitly
554                sent to router for resolving. */
555
556             /** Send query to router */
557             silc_free(query->ids);
558             query->ids = NULL;
559             query->ids_count = 0;
560             silc_fsm_next(fsm, silc_server_st_query_send_router);
561             return SILC_FSM_CONTINUE;
562           }
563         }
564
565         query->ids[query->ids_count] = id;
566         query->ids_count++;
567       }
568     }
569
570     /* Get the max count of reply messages allowed */
571     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
572     if (tmp && tmp_len == sizeof(SilcUInt32))
573       SILC_GET32_MSB(query->reply_count, tmp);
574     break;
575   }
576
577   /** Find entries for query */
578   silc_fsm_next(fsm, silc_server_st_query_find);
579   return SILC_FSM_CONTINUE;
580 }
581
582 /* Find the entries according to the query */
583
584 SILC_FSM_STATE(silc_server_st_query_find)
585 {
586   SilcServerThread thread = fsm_context;
587   SilcServer server = thread->server;
588   SilcServerQuery query = state_context;
589   SilcServerCommand cmd = query->cmd;
590   SilcIDCacheEntry id_entry;
591   SilcID *id;
592   void *entry;
593   int i;
594
595   SILC_LOG_DEBUG(("Finding entries with %s query",
596                   silc_get_command_name(query->querycmd)));
597
598   if (query->nickname) {
599     /* Find by nickname */
600     if (!silc_server_find_clients(server, query->nickname, &query->clients))
601       silc_server_query_add_error(server, query, 1, 1,
602                                   SILC_STATUS_ERR_NO_SUCH_NICK);
603   }
604
605   if (query->server_name) {
606     /* Find server by name */
607     if (!silc_server_find_server_by_name(server, query->server_name, TRUE,
608                                          &id_entry))
609       silc_server_query_add_error(server, query, 1, 2,
610                                   SILC_STATUS_ERR_NO_SUCH_SERVER);
611     else
612       silc_list_add(query->servers, id_entry);
613   }
614
615   if (query->channel_name) {
616     /* Find channel by name */
617     if (!silc_server_find_channel_by_name(server, query->channel_name,
618                                           &id_entry))
619       silc_server_query_add_error(server, query, 1, 3,
620                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL);
621     else
622       silc_list_add(query->channels, id_entry);
623   }
624
625   if (query->ids_count) {
626     /* Find entries by the queried IDs */
627     for (i = 0; i < query->ids_count; i++) {
628       id = &query->ids[i];
629
630       switch (id->type) {
631
632       case SILC_ID_CLIENT:
633         /* Get client entry */
634         if (!silc_server_find_client_by_id(server, &id->u.client_id, TRUE,
635                                            &id_entry)) {
636           silc_server_query_add_error(server, query, 0, i,
637                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
638           continue;
639         }
640
641         silc_list_add(query->clients, id_entry);
642         break;
643
644       case SILC_ID_SERVER:
645         /* Get server entry */
646         if (!silc_server_find_server_by_id(server, &id->u.server_id, TRUE,
647                                            &id_entry)) {
648           silc_server_query_add_error(server, query, 0, i,
649                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
650           continue;
651         }
652
653         silc_list_add(query->servers, id_entry);
654         break;
655
656       case SILC_ID_CHANNEL:
657         /* Get channel entry */
658         if (!silc_server_find_channel_by_id(server, &id->u.channel_id,
659                                             &id_entry)) {
660           silc_server_query_add_error(server, query, 0, i,
661                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
662           continue;
663         }
664
665         silc_list_add(query->channels, id_entry);
666         break;
667
668       default:
669         break;
670       }
671     }
672   }
673
674   /* Check the attributes to narrow down the search by using them. */
675   if (query->attrs) {
676     /** Check user attributes */
677     silc_fsm_next(fsm, silc_server_st_query_check_attrs);
678     return SILC_FSM_CONTINUE;
679   }
680
681   /** Process found entries */
682   silc_fsm_next(fsm, silc_server_st_query_process);
683   return SILC_FSM_CONTINUE;
684 }
685
686 /* Check user attributes to narrow down clients in WHOIS query */
687
688 SILC_FSM_STATE(silc_server_st_query_check_attrs)
689 {
690
691   /** Proecss found entries */
692   silc_fsm_next(fsm, silc_server_st_query_process);
693   return SILC_FSM_CONTINUE;
694 }
695
696 /* Process found entries */
697
698 SILC_FSM_STATE(silc_server_st_query_process)
699 {
700   SilcServerThread thread = fsm_context;
701   SilcServer server = thread->server;
702   SilcServerQuery query = state_context;
703   SilcServerCommand cmd = query->cmd;
704   SilcServerQueryResolve res;
705   SilcIDCacheEntry id_entry;
706   SilcClientEntry client_entry;
707   SilcServerEntry server_entry;
708   SilcChannelEntry channel_entry;
709   SilcID *id;
710   void *entry;
711   int i;
712
713   SILC_LOG_DEBUG(("Process %s query",
714                   silc_get_command_name(query->querycmd)));
715
716   SILC_LOG_DEBUG(("Querying %d clients", silc_list_count(query->clients)));
717   SILC_LOG_DEBUG(("Querying %d servers", silc_list_count(query->servers)));
718   SILC_LOG_DEBUG(("Querying %d channels", silc_list_count(query->channels)));
719
720   /* If nothing was found, then just send the errors */
721   if (!silc_list_count(query->clients) &&
722       !silc_list_count(query->channels) &&
723       !silc_list_count(query->servers)) {
724     /** Nothing found, send errors */
725     silc_fsm_next(fsm, silc_server_st_query_reply);
726     return SILC_FSM_CONTINUE;
727   }
728
729 #if 0
730   /* If caller does not want us to resolve anything (has resolved already)
731      then just continue with sending the reply */
732   if (!resolve) {
733     silc_server_query_send_reply(server, query, clients, clients_count,
734                                  servers, servers_count, channels,
735                                  channels_count);
736     silc_free(clients);
737     silc_free(servers);
738     silc_free(channels);
739     return;
740   }
741 #endif
742
743   /* Now process all found information and if necessary do some more
744      resolving. */
745   switch (query->querycmd) {
746
747   case SILC_COMMAND_WHOIS:
748     silc_list_start(query->clients);
749     while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
750       client_entry = id_entry->context;
751
752       /* Ignore unregistered clients */
753       if (!SILC_IS_REGISTERED(client_entry)) {
754         silc_list_del(query->clients, id_entry);
755         continue;
756       }
757
758       /* If Requested Attributes is set then we always resolve the client
759          information, if not then check whether the entry is complete or not
760          and decide whether we need to resolve the missing information. */
761       if (!query->attrs) {
762
763         /* Even if nickname and stuff are present, we may need to resolve
764            the entry on normal server. */
765         if (client_entry->nickname && client_entry->username &&
766             client_entry->userinfo) {
767
768           /* If we are router, client is local to us, or client is on channel
769              we do not need to resolve the client information. */
770           if (server->server_type != SILC_SERVER ||
771               SILC_IS_LOCAL(client_entry)||
772               silc_hash_table_count(client_entry->channels) ||
773               query->resolved)
774             continue;
775         }
776       }
777
778       /* Remove the NOATTR status periodically */
779       if (client_entry->data.noattr &&
780           client_entry->updated + 600 < time(NULL))
781         client_entry->data.noattr = FALSE;
782
783       /* When requested attributes is present and local client is detached
784          we cannot send the command to the client, we'll reply on behalf of
785          the client instead. */
786       if (query->attrs && SILC_IS_LOCAL(client_entry) &&
787           (client_entry->mode & SILC_UMODE_DETACHED ||
788            client_entry->data.noattr))
789         continue;
790
791 #if 0
792       /* If attributes are present in query, and in the entry and we have
793          done resolvings already we don't need to resolve anymore */
794       if (query->resolved && query->attrs && client_entry->attrs)
795         continue;
796 #endif
797
798       /* Mark this entry to be resolved */
799       silc_list_add(query->resolve, id_entry);
800     }
801     break;
802
803   case SILC_COMMAND_WHOWAS:
804     silc_list_start(query->clients);
805     while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
806       client_entry = id_entry->context;
807
808       /* Take only unregistered clients */
809       if (SILC_IS_REGISTERED(client_entry)) {
810         silc_list_del(query->clients, id_entry);
811         continue;
812       }
813
814       /* If both nickname and username are present no resolving is needed */
815       if (client_entry->nickname && client_entry->username)
816         continue;
817
818       /* Mark this entry to be resolved */
819       silc_list_add(query->resolve, id_entry);
820     }
821     break;
822
823   case SILC_COMMAND_IDENTIFY:
824     silc_list_start(query->clients);
825     while ((id_entry = silc_list_get(query->clients)) != SILC_LIST_END) {
826       client_entry = id_entry->context;
827
828       /* Ignore unregistered clients */
829       if (!SILC_IS_REGISTERED(client_entry))
830         continue;
831
832       /* Even if nickname is present, we may need to resolve the entry
833          on normal server. */
834       if (client_entry->nickname) {
835
836         /* If we are router, client is local to us, or client is on channel
837            we do not need to resolve the client information. */
838         if (server->server_type != SILC_SERVER ||
839             SILC_IS_LOCAL(client_entry)||
840             silc_hash_table_count(client_entry->channels) ||
841             query->resolved)
842           continue;
843       }
844
845       /* Mark this entry to be resolved */
846       silc_list_add(query->resolve, id_entry);
847     }
848     break;
849   }
850
851   /* If we need to resolve entries, do it now */
852   if (silc_list_count(query->resolve)) {
853     /** Resolve entries */
854     silc_fsm_next(fsm, silc_server_st_query_resolve);
855     return SILC_FSM_CONTINUE;
856   }
857
858   /** Send reply to query */
859   silc_fsm_next(fsm, silc_server_st_query_reply);
860   return SILC_FSM_CONTINUE;
861 }
862
863 /* Resolve incomplete client entries.  Other types of entries need not
864    resolving. */
865
866 SILC_FSM_STATE(silc_server_st_query_resolve)
867 {
868   SilcServerThread thread = fsm_context;
869   SilcServer server = thread->server;
870   SilcServerQuery query = state_context;
871   SilcArgumentPayload cmd_args = silc_command_get_args(query->cmd->payload);
872   SilcServerQueryResolve res;
873   SilcIDCacheEntry id_entry;
874   unsigned char args[256][28];
875   SilcUInt32 arg_lens[256], arg_types[256], argc = 0;
876   SilcBuffer res_cmd;
877   int i;
878
879   SILC_LOG_DEBUG(("Resolve incomplete entries"));
880
881   silc_list_start(query->resolve);
882   while ((id_entry = silc_list_get(query->resolve)) != SILC_LIST_END) {
883     client_entry = id_entry->context;
884
885     /* If entry is being resolved, attach to that resolving */
886     if (client_entry->data.resolving) {
887       res = silc_calloc(1, sizeof(*res));
888       if (!res)
889         continue;
890
891       silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE);
892       res->stream = client_entry->stream;
893
894       res->pending =
895         silc_server_command_pending(thread, client_entry->resolve_cmd_ident);
896       if (!res->pending) {
897         SILC_LOG_ERROR(("BUG: No pending command for resolving client entry"));
898         continue;
899       }
900
901       res->attached = TRUE;
902       silc_list_add(query->resolvings, res);
903       continue;
904     }
905
906     /* Check if we have resolving destination already set */
907     silc_list_start(query->resolvings);
908     while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END)
909       if (res->stream == client_entry->stream && !res->attached)
910         break;
911
912     if (!res) {
913       /* Create new resolving context */
914       res = silc_calloc(1, sizeof(*res));
915       if (!res)
916         continue;
917
918       silc_fsm_thread_init(&res->thread, fsm, res, NULL, NULL, FALSE);
919       res->stream = client_entry->stream;
920
921       res->pending =
922         silc_server_command_pending(thread, silc_server_cmd_ident(server));
923       if (!res->pending)
924         continue;
925
926       silc_list_add(query->resolvings, res);
927     }
928
929     /* Mark the entry as being resolved */
930     client_entry->data.resolving = TRUE;
931     client_entry->data.resolved = FALSE;
932     client_entry->resolve_cmd_ident = res->pending->cmd_ident;
933     client_entry->updated = time(NULL);
934
935     if (SILC_IS_LOCAL(client_entry))
936       res->local = TRUE;
937
938     switch (query->querycmd) {
939     case SILC_COMMAND_WHOIS:
940     case SILC_COMMAND_IDENTIFY:
941       res->ids = silc_realloc(res->ids, sizeof(*res->ids) *
942                               (res->ids_count + 1));
943       if (!res->ids)
944         continue;
945
946       res->ids[res->ids_count++].u.client_id = client_entry->id;
947       break;
948
949     case SILC_COMMAND_WHOWAS:
950       break;
951     }
952   }
953
954   SILC_LOG_DEBUG(("Sending the resolvings"));
955
956   /* Send the resolvings */
957   silc_list_start(query->resolvings);
958   while ((res = silc_list_get(query->resolvings)) != SILC_LIST_END) {
959
960     if (!res->attached) {
961
962       switch (query->querycmd) {
963       case SILC_COMMAND_WHOIS:
964       case SILC_COMMAND_IDENTIFY:
965
966         /* If Requested Attributes were present put them to this resolving */
967         if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
968           arg_types[argc] = 3;
969           args[argc] = silc_argument_get_arg_type(cmd_args, 3,
970                                                   &arg_lens[argc]);
971           argc++;
972         }
973
974         /* Encode IDs */
975         for (i = 0; i < res->ids_count; i++) {
976           arg_types[argc] = (query->querycmd == SILC_COMMAND_WHOIS ?
977                              4 + i : 5 + i);
978           silc_id_id2str(&res->ids[argc].u.client_id, SILC_ID_CLIENT,
979                          args[argc], sizeof(args[argc]), &arg_lens[argc]);
980           argc++;
981           if (i + 1 > 255)
982             break;
983         }
984
985         /* Send the command */
986         res_cmd = silc_command_payload_encode(query->querycmd, argc,
987                                               args, arg_lens, arg_types,
988                                               res->pending->cmd_ident);
989         if (!res_cmd) {
990           /** No memory */
991           silc_server_query_send_error(server, query,
992                                        SILC_STATUS_ERR_RESOURCE_LIMIT, 0);
993           silc_fsm_next(fsm, silc_server_st_query_error);
994           return SILC_FSM_CONTINUE;
995         }
996
997         silc_packet_send(res->stream, SILC_PACKET_COMMAND, 0,
998                          res_cmd->data, silc_buffer_send(res_cmd));
999         silc_buffer_free(res_cmd);
1000         silc_free(res->ids);
1001         res->ids = NULL;
1002
1003         /* Statistics */
1004         server->stat.commands_sent++;
1005         break;
1006
1007       case SILC_COMMAND_WHOWAS:
1008         /* Send WHOWAS command */
1009         silc_server_send_command(server, res->stream, query->querycmd,
1010                                  res->pending->cmd_ident, 1,
1011                                  1, query->nickname, strlen(query->nickname));
1012         break;
1013       }
1014     }
1015
1016     /*** Resolve */
1017     silc_fsm_set_state_context(&res->thread, query);
1018     silc_fsm_start_sync(&res->thread, silc_server_st_query_wait_resolve);
1019   }
1020
1021   /** Wait all resolvings */
1022   silc_fsm_next(fsm, silc_server_st_query_resolved);
1023   return SILC_FSM_CONTINUE;
1024 }
1025
1026 /* Wait for resolving command reply */
1027
1028 SILC_FSM_STATE(silc_server_st_query_wait_resolve)
1029 {
1030   SilcServerQueryResolve res = fsm_context;
1031   SilcServerQuery query = state_context;
1032   SilcBool timedout;
1033
1034   /* Wait here for the reply */
1035   SILC_FSM_EVENT_TIMEDWAIT(&res->pending->wait_reply,
1036                           res->local ? 3 : 10, 0, &timedout);
1037
1038
1039
1040   silc_list_del(query->resolvings, res);
1041   silc_server_command_pending_free(res->pending);
1042   silc_free(res);
1043
1044   /* Signal main thread that reply was received */
1045   SILC_FSM_EVENT_SIGNAL(&query->wait_resolve);
1046
1047   return SILC_FSM_FINISH;
1048 }
1049
1050 /* Wait here that all resolvings has been received */
1051
1052 SILC_FSM_STATE(silc_server_st_query_resolved)
1053 {
1054   SilcServerThread thread = fsm_context;
1055   SilcServer server = thread->server;
1056   SilcServerQuery query = state_context;
1057   SilcServerCommand cmd = query->cmd;
1058
1059   /* Wait here until all resolvings has arrived */
1060   SILC_FSM_EVENT_WAIT(&query->wait_resolve);
1061   if (silc_list_count(query->resolvings) > 0)
1062     return SILC_FSM_CONTINUE;
1063
1064 }
1065
1066 /* Send the reply to the query. */
1067
1068 SILC_FSM_STATE(silc_server_st_query_reply)
1069 {
1070   SilcServerThread thread = fsm_context;
1071   SilcServer server = thread->server;
1072   SilcServerQuery query = state_context;
1073   SilcServerCommand cmd = query->cmd;
1074   SilcIDCacheEntry id_entry;
1075
1076 }