Renamed silc_server_get_client_resolve to silc_server_query_client,
[silc.git] / apps / silcd / server_query.c
1 /*
2
3   server_query.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12   
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "serverincludes.h"
22 #include "server_internal.h"
23
24 typedef struct {
25   void *id;
26   SilcIdType id_type;
27 } *SilcServerQueryID;
28
29 typedef struct {
30   SilcUInt32 index;                 /* Index to IDs */
31   bool from_cmd;                    /* TRUE if `index' is from command args,
32                                        otherwise from query->ids */
33   SilcStatus error;                 /* The actual error */
34 } *SilcServerQueryError;
35
36 typedef struct {
37   SilcCommand querycmd;             /* Query command */
38   SilcServerCommandContext cmd;     /* Command context for query */
39
40   char *nickname;                   /* Queried nickname */
41   char *nick_server;                /* Queried nickname's server */
42   char *server_name;                /* Queried server name */
43   char *channel_name;               /* Queried channel name */
44   SilcServerQueryID ids;            /* Queried IDs */
45   SilcUInt32 ids_count;             /* number of queried IDs */
46   SilcUInt32 reply_count;           /* Requested reply count */
47   SilcDList attrs;                  /* Requested Attributes in WHOIS */
48
49   SilcServerQueryError errors;      /* Query errors */
50   SilcUInt32 errors_count;          /* number of errors */
51 } *SilcServerQuery;
52
53 void silc_server_query_free(SilcServerQuery query);
54 bool silc_server_query_check_error(SilcServer server,
55                                    SilcServerQuery query,
56                                    SilcServerCommandReplyContext cmdr);
57 void silc_server_query_send_error(SilcServer server,
58                                   SilcServerQuery query,
59                                   SilcStatus error, ...);
60 void silc_server_query_add_error(SilcServer server,
61                                  SilcServerQuery query,
62                                  bool from_cmd,
63                                  SilcUInt32 index,
64                                  SilcStatus error);
65 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
66 void silc_server_query_send_router_reply(void *context, void *reply);
67 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
68 void silc_server_query_process(SilcServer server, SilcServerQuery query);
69
70
71 /* Free the query context structure and all allocated resources. */
72
73 void silc_server_query_free(SilcServerQuery query)
74 {
75   int i;
76
77   silc_server_command_free(query->cmd);
78
79   silc_free(query->nickname);
80   silc_free(query->nick_server);
81   silc_free(query->server_name);
82   silc_free(query->channel_name);
83
84   for (i = 0; i < query->ids_count; i++)
85     silc_free(query->ids[i].id);
86   silc_free(query->ids);
87
88   if (query->attrs)
89     silc_attribute_payload_list_free(query->attrs);
90
91   silc_free(query->errors);
92
93   memset(query, 'F', sizeof(*query));
94   silc_free(query);
95 }
96
97 /* Check whether command reply contained error, and reply the error to
98    the original sender if it occurred. */
99
100 bool silc_server_query_check_error(SilcServer server,
101                                    SilcServerQuery query,
102                                    SilcServerCommandReplyContext cmdr)
103 {
104   if (!cmdr)
105     return FALSE;
106
107   if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
108     SilcBuffer buffer;
109
110     /* Send the same command reply payload which contains the error */
111     silc_command_set_command(cmdr->payload, query->querycmd);
112     silc_command_set_ident(cmdr->payload,
113                            silc_command_get_ident(query->cmd->payload));
114     buffer = silc_command_payload_encode_payload(cmdr->payload);
115     silc_server_packet_send(server, query->cmd->sock,
116                             SILC_PACKET_COMMAND_REPLY, 0, 
117                             buffer->data, buffer->len, FALSE);
118     silc_buffer_free(buffer);
119     return TRUE;
120   }
121
122   return FALSE;
123 }
124
125 /* Send error reply indicated by the `error' to the original sender of
126    the query. */
127
128 void silc_server_query_send_error(SilcServer server,
129                                   SilcServerQuery query,
130                                   SilcStatus error, ...)
131 {
132   va_list va;
133   SilcBuffer packet;
134   unsigned char *data = NULL;
135   SilcUInt32 data_len = 0, data_type = 0, argc = 0;
136
137   va_start(va, error);
138   data_type = va_arg(va, SilcUInt32);
139   if (data_type) {
140     argc = 1;
141     data = va_arg(va, unsigned char *);
142     data_len = va_arg(va, SilcUInt32);
143   }
144
145   /* Send the command reply with error */
146   packet = silc_command_reply_payload_encode_va(
147                                query->querycmd, error, 0,
148                                silc_command_get_ident(query->cmd->payload),
149                                argc, data_type, data, data_len);
150   silc_server_packet_send(server, query->cmd->sock,
151                           SILC_PACKET_COMMAND_REPLY, 0, 
152                           packet->data, packet->len, FALSE);
153
154   silc_buffer_free(packet);
155   va_end(va);
156 }
157
158 /* Add error to error list.  Multiple errors may occur during the query
159    processing and this function can be used to add one error.  The
160    `type_index' is the index to the command context which includes the
161    argument which caused the error. */
162
163 void silc_server_query_add_error(SilcServer server,
164                                  SilcServerQuery query,
165                                  bool from_cmd,
166                                  SilcUInt32 index,
167                                  SilcStatus error)
168 {
169   query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
170                                (query->errors_count + 1));
171   if (!query->errors)
172     return;
173   query->errors[query->errors_count].index = index;
174   query->errors[query->errors_count].from_cmd = from_cmd;
175   query->errors[query->errors_count].error = error;
176   query->errors_count++;
177 }
178
179 /* Processes query as command.  The `query' is the command that is
180    being processed indicated by the `cmd'.  The `query' can be one of
181    the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
182    SILC_COMMAND_IDENTIFY.  This function handles the reply sending
183    to the entity who sent this query to us automatically.  Returns
184    TRUE if the query is being processed or FALSE on error. */
185
186 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
187                                SilcServerCommandContext cmd)
188 {
189   SilcServerQuery query;
190
191   switch (querycmd) {
192
193   case SILC_COMMAND_WHOIS:
194     {
195       query = silc_calloc(1, sizeof(*query));
196       query->querycmd = querycmd;
197       query->cmd = silc_server_command_dup(cmd);
198
199       /* If we are normal server and query contains nickname, send it
200          directly to router. */
201       if (server->server_type == SILC_SERVER && !server->standalone &&
202           silc_argument_get_arg_type(cmd->args, 1, NULL)) {
203         silc_server_query_send_router(server, query);
204         break;
205       }
206
207       /* Now parse the WHOIS query */
208       silc_server_query_parse(server, query);
209     }
210     break;
211
212   case SILC_COMMAND_WHOWAS:
213     {
214       query = silc_calloc(1, sizeof(*query));
215       query->querycmd = querycmd;
216       query->cmd = silc_server_command_dup(cmd);
217
218       /* WHOWAS query is always sent to router if we are normal server */
219       if (server->server_type == SILC_SERVER && !server->standalone) {
220         silc_server_query_send_router(server, query);
221         break;
222       }
223
224       /* Now parse the WHOWAS query */
225       silc_server_query_parse(server, query);
226     }
227     break;
228
229   case SILC_COMMAND_IDENTIFY:
230     {
231       query = silc_calloc(1, sizeof(*query));
232       query->querycmd = querycmd;
233       query->cmd = silc_server_command_dup(cmd);
234
235       /* If we are normal server and query does not contain IDs, send it
236          directly to router (it contains nickname, server name or channel
237          name). */
238       if (server->server_type == SILC_SERVER && !server->standalone &&
239           !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
240         silc_server_query_send_router(server, query);
241         break;
242       }
243
244       /* Now parse the IDENTIFY query */
245       silc_server_query_parse(server, query);
246     }
247     break;
248
249   default:
250     SILC_LOG_ERROR(("Bad query using %d command", querycmd));
251     return FALSE;
252   }
253
254   return TRUE;
255 }
256
257 /* Send the received query to our primary router since we could not
258    handle the query directly.  We will reprocess the query after our
259    router replies back. */
260
261 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
262 {
263   SilcBuffer tmpbuf;
264   SilcUInt16 old_ident;
265
266   /* Send WHOIS command to our router */
267   old_ident = silc_command_get_ident(query->cmd->payload);
268   silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
269   tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
270   silc_server_packet_send(server, 
271                           SILC_PRIMARY_ROUTE(server),
272                           SILC_PACKET_COMMAND, 0,
273                           tmpbuf->data, tmpbuf->len, TRUE);
274   silc_command_set_ident(query->cmd->payload, old_ident);
275   silc_buffer_free(tmpbuf);
276
277   /* Continue parsing the query after received reply from router */
278   silc_server_command_pending(server, query->querycmd, server->cmd_ident,
279                               silc_server_query_send_router_reply, query);
280 }
281
282 /* Reply callback called after primary router has replied to our initial
283    sending of the query to it.  We will proceed the query in this function. */
284
285 void silc_server_query_send_router_reply(void *context, void *reply)
286 {
287   SilcServerQuery query = context;
288   SilcServer server = query->cmd->server;
289
290   /* Check if router sent error reply */
291   if (!silc_server_query_check_error(server, query, reply)) {
292     silc_server_query_free(query);
293     return;
294   }
295
296   /* Continue with parsing */
297   silc_server_query_parse(server, query);
298 }
299
300 /* Parse the command query and start processing the queries in detail. */
301
302 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
303 {
304   SilcServerCommandContext cmd = query->cmd;
305   unsigned char *tmp;
306   SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
307   void *id;
308   SilcIdType id_type;
309   int i;
310
311   switch (query->querycmd) {
312
313   case SILC_COMMAND_WHOIS:
314     {
315       /* Get Client IDs if present. Take IDs always instead of nickname. */
316       tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
317       if (!tmp) {
318
319         /* Get nickname */
320         tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
321         if (!tmp) {
322           silc_server_query_send_error(server, query,
323                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
324           silc_server_query_free(query);
325           return;
326         }
327
328         /* Get the nickname@server string and parse it */
329         if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
330           silc_server_query_send_error(server, query,
331                                        SILC_STATUS_ERR_BAD_NICKNAME, 0);
332           silc_server_query_free(query);
333           return;
334         }
335
336       } else {
337         /* Parse the IDs included in the query */
338         query->ids = silc_calloc(argc, sizeof(*query->ids));
339
340         for (i = 0; i < argc; i++) {
341           tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
342           if (!tmp)
343             continue;
344
345           id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
346           if (!id) {
347             silc_server_query_add_error(server, query, TRUE, i + 4,
348                                         SILC_STATUS_ERR_BAD_CLIENT_ID);
349             continue;
350           }
351
352           query->ids[query->ids_count].id = id;
353           query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
354           query->ids_count++;
355         }
356       }
357
358       /* Get the max count of reply messages allowed */
359       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
360       if (tmp && tmp_len == sizeof(SilcUInt32))
361         SILC_GET32_MSB(query->reply_count, tmp);
362
363       /* Get requested attributes if set */
364       tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
365       if (tmp)
366         query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
367     }
368     break;
369
370   case SILC_COMMAND_WHOWAS:
371     {
372       /* Get nickname */
373       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
374       if (!tmp) {
375         silc_server_query_send_error(server, query,
376                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
377         silc_server_query_free(query);
378         return;
379       }
380
381       /* Get the nickname@server string and parse it */
382       if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
383         silc_server_query_send_error(server, query,
384                                      SILC_STATUS_ERR_BAD_NICKNAME, 0);
385         silc_server_query_free(query);
386         return;
387       }
388
389       /* Get the max count of reply messages allowed */
390       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
391       if (tmp && tmp_len == sizeof(SilcUInt32))
392         SILC_GET32_MSB(query->reply_count, tmp);
393     }
394     break;
395
396   case SILC_COMMAND_IDENTIFY:
397     {
398       /* Get IDs if present. Take IDs always instead of names. */
399       tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
400       if (!tmp) {
401
402         /* Try get nickname */
403         tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
404         if (tmp) {
405           /* Get the nickname@server string and parse it */
406           if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
407             silc_server_query_add_error(server, query, TRUE, 1,
408                                         SILC_STATUS_ERR_BAD_NICKNAME);
409         }
410
411         /* Try get server name */
412         tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
413         if (tmp)
414           query->server_name = silc_memdup(tmp, tmp_len);
415
416         /* Get channel name */
417         tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
418         if (tmp)
419           query->channel_name = silc_memdup(tmp, tmp_len);
420
421       } else {
422         /* Parse the IDs included in the query */
423         query->ids = silc_calloc(argc, sizeof(*query->ids));
424
425         for (i = 0; i < argc; i++) {
426           tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
427           if (!tmp)
428             continue;
429
430           id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
431           if (!id) {
432             silc_server_query_add_error(server, query, TRUE, i + 5,
433                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
434             continue;
435           }
436
437           query->ids[query->ids_count].id = id;
438           query->ids[query->ids_count].id_type = id_type;
439           query->ids_count++;
440         }
441       }
442
443       /* Get the max count of reply messages allowed */
444       tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
445       if (tmp && tmp_len == sizeof(SilcUInt32))
446         SILC_GET32_MSB(query->reply_count, tmp);
447     }
448     break;
449   }
450
451   /* Start processing the query information */
452   silc_server_query_process(server, query);
453 }
454
455 /* Processes the parsed query.  This does the actual finding of the
456    queried information and prepares for sending reply to the original
457    sender of the query command.  It is guaranteed that this function
458    (which may be slow) is called only once for entire query. */
459
460 void silc_server_query_process(SilcServer server, SilcServerQuery query)
461 {
462   SilcServerCommandContext cmd = query->cmd;
463   bool check_global = FALSE;
464   void *entry;
465   SilcClientEntry *clients = NULL;
466   SilcChannelEntry *channels = NULL;
467   SilcServerEntry *servers = NULL;
468   SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
469   int i;
470
471   /* Check global lists if query is coming from client or we are not
472      normal server (we know global information). */
473   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
474     check_global = TRUE;
475   else if (server->server_type != SILC_SERVER)
476     check_global = TRUE;
477
478   if (query->nickname) {
479     /* Get all clients matching nickname from local list */
480     if (!silc_idlist_get_clients_by_hash(server->local_list, 
481                                          query->nickname, server->md5hash,
482                                          &clients, &clients_count))
483       silc_idlist_get_clients_by_nickname(server->local_list, 
484                                           query->nickname,
485                                           query->nick_server,
486                                           &clients, &clients_count);
487
488     /* Check global list as well */
489     if (check_global) {
490       if (!silc_idlist_get_clients_by_hash(server->global_list, 
491                                            query->nickname, server->md5hash,
492                                            &clients, &clients_count))
493         silc_idlist_get_clients_by_nickname(server->global_list, 
494                                             query->nickname,
495                                             query->nick_server,
496                                             &clients, &clients_count);
497     }
498
499     if (!clients)
500       silc_server_query_add_error(server, query, TRUE, 1,
501                                   SILC_STATUS_ERR_NO_SUCH_NICK);
502   }
503
504   if (query->server_name) {
505     /* Find server by name */
506     entry = silc_idlist_find_server_by_name(server->local_list,
507                                             query->server_name, TRUE, NULL);
508     if (!entry && check_global)
509       entry = silc_idlist_find_server_by_name(server->global_list,
510                                               query->server_name, TRUE, NULL);
511     if (entry) {
512       servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
513       servers[servers_count++] = (SilcServerEntry)entry;
514     }
515
516     if (!servers)
517       silc_server_query_add_error(server, query, TRUE, 2,
518                                   SILC_STATUS_ERR_NO_SUCH_SERVER);
519   }
520
521   if (query->channel_name) {
522     /* Find channel by name */
523     entry = silc_idlist_find_channel_by_name(server->local_list,
524                                              query->channel_name, NULL);
525     if (!entry && check_global)
526       entry = silc_idlist_find_channel_by_name(server->global_list,
527                                                query->channel_name, NULL);
528     if (entry) {
529       channels = silc_realloc(channels, sizeof(*channels) * 
530                               (channels_count + 1));
531       channels[channels_count++] = (SilcChannelEntry)entry;
532     }
533
534     if (!channels)
535       silc_server_query_add_error(server, query, TRUE, 3,
536                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL);
537   }
538
539   if (query->ids_count) {
540     /* Find entries by the queried IDs */
541     for (i = 0; i < query->ids_count; i++) {
542       void *id = query->ids[i].id;
543       if (!id)
544         continue;
545
546       switch (query->ids[i].id_type) {
547
548       case SILC_ID_CLIENT:
549         /* Get client entry */
550         entry = silc_idlist_find_client_by_id(server->local_list, 
551                                               id, TRUE, NULL);
552         if (!entry && check_global)
553           entry = silc_idlist_find_client_by_id(server->global_list, 
554                                                 id, TRUE, NULL);
555         if (!entry) {
556           silc_server_query_add_error(server, query, FALSE, i,
557                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
558           continue;
559         }
560
561         clients = silc_realloc(clients, sizeof(*clients) * 
562                                (clients_count + 1));
563         clients[clients_count++] = (SilcClientEntry)entry;
564         break;
565
566       case SILC_ID_SERVER:
567         /* Get server entry */
568         entry = silc_idlist_find_server_by_id(server->local_list, 
569                                               id, TRUE, NULL);
570         if (!entry && check_global)
571           entry = silc_idlist_find_server_by_id(server->global_list, 
572                                                 id, TRUE, NULL);
573         if (!entry) {
574           silc_server_query_add_error(server, query, FALSE, i,
575                                       SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
576           continue;
577         }
578
579         servers = silc_realloc(servers, sizeof(*servers) * 
580                                (servers_count + 1));
581         servers[servers_count++] = (SilcServerEntry)entry;
582         break;
583
584       case SILC_ID_CHANNEL:
585         /* Get channel entry */
586         entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
587         if (!entry && check_global)
588           entry = silc_idlist_find_channel_by_id(server->global_list, id,
589                                                  NULL);
590         if (!entry) {
591           silc_server_query_add_error(server, query, FALSE, i,
592                                       SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
593           continue;
594         }
595
596         channels = silc_realloc(channels, sizeof(*channels) * 
597                                 (channels_count + 1));
598         channels[channels_count++] = (SilcChannelEntry)entry;
599         break;
600
601       default:
602         break;
603       }
604     }
605   }
606
607   /* If nothing was found, then just send the errors */
608   if (!clients && !channels && !servers) {
609
610     silc_server_query_free(query);
611     return;
612   }
613
614   /* Now process all found information and if necessary do some more
615      querying. */
616
617 }
618
619 /* Find client by the Client ID indicated by the `client_id', and if not
620    found then query it by using WHOIS command.  The client information
621    is also resolved if the cached information is incomplete or if the
622    `always_resolve' is set to TRUE.  The indication whether requested
623    client was being resolved is saved into `resolved'.  If the client
624    is not being resolved its entry is returned by this function.  NULL
625    is returned if client is resolved. */
626
627 SilcClientEntry silc_server_query_client(SilcServer server,
628                                          const SilcClientID *client_id,
629                                          bool always_resolve,
630                                          bool *resolved)
631 {
632   SilcClientEntry client;
633
634   if (resolved)
635     *resolved = FALSE;
636
637   client = silc_idlist_find_client_by_id(server->local_list,
638                                          (SilcClientID *)client_id,
639                                          TRUE, NULL);
640   if (!client) {
641     client = silc_idlist_find_client_by_id(server->global_list,
642                                            (SilcClientID *)client_id,
643                                            TRUE, NULL);
644     if (!client && server->server_type == SILC_ROUTER)
645       return NULL;
646   }
647
648   if (!client && server->standalone)
649     return NULL;
650
651   if (!client || !client->nickname || !client->username ||
652       always_resolve) {
653     SilcBuffer buffer, idp;
654
655     if (client) {
656       client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
657       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
658       client->resolve_cmd_ident = ++server->cmd_ident;
659     }
660
661     idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
662     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
663                                             server->cmd_ident, 1,
664                                             4, idp->data, idp->len);
665     silc_server_packet_send(server, client ? client->router->connection :
666                             SILC_PRIMARY_ROUTE(server),
667                             SILC_PACKET_COMMAND, 0,
668                             buffer->data, buffer->len, FALSE);
669     silc_buffer_free(idp);
670     silc_buffer_free(buffer);
671
672     if (resolved)
673       *resolved = TRUE;
674
675     return NULL;
676   }
677
678   return client;
679 }