5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002 Pekka Riikonen
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.
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.
21 #include "serverincludes.h"
22 #include "server_internal.h"
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;
37 SilcCommand querycmd; /* Query command */
38 SilcServerCommandContext cmd; /* Command context for query */
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 */
49 SilcServerQueryError errors; /* Query errors */
50 SilcUInt32 errors_count; /* number of errors */
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,
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);
71 /* Free the query context structure and all allocated resources. */
73 void silc_server_query_free(SilcServerQuery query)
77 silc_server_command_free(query->cmd);
79 silc_free(query->nickname);
80 silc_free(query->nick_server);
81 silc_free(query->server_name);
82 silc_free(query->channel_name);
84 for (i = 0; i < query->ids_count; i++)
85 silc_free(query->ids[i].id);
86 silc_free(query->ids);
89 silc_attribute_payload_list_free(query->attrs);
91 silc_free(query->errors);
93 memset(query, 'F', sizeof(*query));
97 /* Check whether command reply contained error, and reply the error to
98 the original sender if it occurred. */
100 bool silc_server_query_check_error(SilcServer server,
101 SilcServerQuery query,
102 SilcServerCommandReplyContext cmdr)
107 if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
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);
125 /* Send error reply indicated by the `error' to the original sender of
128 void silc_server_query_send_error(SilcServer server,
129 SilcServerQuery query,
130 SilcStatus error, ...)
134 unsigned char *data = NULL;
135 SilcUInt32 data_len = 0, data_type = 0, argc = 0;
138 data_type = va_arg(va, SilcUInt32);
141 data = va_arg(va, unsigned char *);
142 data_len = va_arg(va, SilcUInt32);
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);
154 silc_buffer_free(packet);
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. */
163 void silc_server_query_add_error(SilcServer server,
164 SilcServerQuery query,
169 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
170 (query->errors_count + 1));
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++;
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. */
186 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
187 SilcServerCommandContext cmd)
189 SilcServerQuery query;
193 case SILC_COMMAND_WHOIS:
195 query = silc_calloc(1, sizeof(*query));
196 query->querycmd = querycmd;
197 query->cmd = silc_server_command_dup(cmd);
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);
207 /* Now parse the WHOIS query */
208 silc_server_query_parse(server, query);
212 case SILC_COMMAND_WHOWAS:
214 query = silc_calloc(1, sizeof(*query));
215 query->querycmd = querycmd;
216 query->cmd = silc_server_command_dup(cmd);
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);
224 /* Now parse the WHOWAS query */
225 silc_server_query_parse(server, query);
229 case SILC_COMMAND_IDENTIFY:
231 query = silc_calloc(1, sizeof(*query));
232 query->querycmd = querycmd;
233 query->cmd = silc_server_command_dup(cmd);
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
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);
244 /* Now parse the IDENTIFY query */
245 silc_server_query_parse(server, query);
250 SILC_LOG_ERROR(("Bad query using %d command", querycmd));
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. */
261 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
264 SilcUInt16 old_ident;
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);
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);
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. */
285 void silc_server_query_send_router_reply(void *context, void *reply)
287 SilcServerQuery query = context;
288 SilcServer server = query->cmd->server;
290 /* Check if router sent error reply */
291 if (!silc_server_query_check_error(server, query, reply)) {
292 silc_server_query_free(query);
296 /* Continue with parsing */
297 silc_server_query_parse(server, query);
300 /* Parse the command query and start processing the queries in detail. */
302 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
304 SilcServerCommandContext cmd = query->cmd;
306 SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
311 switch (query->querycmd) {
313 case SILC_COMMAND_WHOIS:
315 /* Get Client IDs if present. Take IDs always instead of nickname. */
316 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
320 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
322 silc_server_query_send_error(server, query,
323 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
324 silc_server_query_free(query);
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);
337 /* Parse the IDs included in the query */
338 query->ids = silc_calloc(argc, sizeof(*query->ids));
340 for (i = 0; i < argc; i++) {
341 tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
345 id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
347 silc_server_query_add_error(server, query, TRUE, i + 4,
348 SILC_STATUS_ERR_BAD_CLIENT_ID);
352 query->ids[query->ids_count].id = id;
353 query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
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);
363 /* Get requested attributes if set */
364 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
366 query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
370 case SILC_COMMAND_WHOWAS:
373 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
375 silc_server_query_send_error(server, query,
376 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
377 silc_server_query_free(query);
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);
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);
396 case SILC_COMMAND_IDENTIFY:
398 /* Get IDs if present. Take IDs always instead of names. */
399 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
402 /* Try get nickname */
403 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
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);
411 /* Try get server name */
412 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
414 query->server_name = silc_memdup(tmp, tmp_len);
416 /* Get channel name */
417 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
419 query->channel_name = silc_memdup(tmp, tmp_len);
422 /* Parse the IDs included in the query */
423 query->ids = silc_calloc(argc, sizeof(*query->ids));
425 for (i = 0; i < argc; i++) {
426 tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
430 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
432 silc_server_query_add_error(server, query, TRUE, i + 5,
433 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
437 query->ids[query->ids_count].id = id;
438 query->ids[query->ids_count].id_type = id_type;
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);
451 /* Start processing the query information */
452 silc_server_query_process(server, query);
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. */
460 void silc_server_query_process(SilcServer server, SilcServerQuery query)
462 SilcServerCommandContext cmd = query->cmd;
463 bool check_global = FALSE;
465 SilcClientEntry *clients = NULL;
466 SilcChannelEntry *channels = NULL;
467 SilcServerEntry *servers = NULL;
468 SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
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)
475 else if (server->server_type != SILC_SERVER)
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,
486 &clients, &clients_count);
488 /* Check global list as well */
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,
496 &clients, &clients_count);
500 silc_server_query_add_error(server, query, TRUE, 1,
501 SILC_STATUS_ERR_NO_SUCH_NICK);
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);
512 servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
513 servers[servers_count++] = (SilcServerEntry)entry;
517 silc_server_query_add_error(server, query, TRUE, 2,
518 SILC_STATUS_ERR_NO_SUCH_SERVER);
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);
529 channels = silc_realloc(channels, sizeof(*channels) *
530 (channels_count + 1));
531 channels[channels_count++] = (SilcChannelEntry)entry;
535 silc_server_query_add_error(server, query, TRUE, 3,
536 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
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;
546 switch (query->ids[i].id_type) {
549 /* Get client entry */
550 entry = silc_idlist_find_client_by_id(server->local_list,
552 if (!entry && check_global)
553 entry = silc_idlist_find_client_by_id(server->global_list,
556 silc_server_query_add_error(server, query, FALSE, i,
557 SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
561 clients = silc_realloc(clients, sizeof(*clients) *
562 (clients_count + 1));
563 clients[clients_count++] = (SilcClientEntry)entry;
567 /* Get server entry */
568 entry = silc_idlist_find_server_by_id(server->local_list,
570 if (!entry && check_global)
571 entry = silc_idlist_find_server_by_id(server->global_list,
574 silc_server_query_add_error(server, query, FALSE, i,
575 SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
579 servers = silc_realloc(servers, sizeof(*servers) *
580 (servers_count + 1));
581 servers[servers_count++] = (SilcServerEntry)entry;
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,
591 silc_server_query_add_error(server, query, FALSE, i,
592 SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
596 channels = silc_realloc(channels, sizeof(*channels) *
597 (channels_count + 1));
598 channels[channels_count++] = (SilcChannelEntry)entry;
607 /* If nothing was found, then just send the errors */
608 if (!clients && !channels && !servers) {
610 silc_server_query_free(query);
614 /* Now process all found information and if necessary do some more
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. */
627 SilcClientEntry silc_server_query_client(SilcServer server,
628 const SilcClientID *client_id,
632 SilcClientEntry client;
637 client = silc_idlist_find_client_by_id(server->local_list,
638 (SilcClientID *)client_id,
641 client = silc_idlist_find_client_by_id(server->global_list,
642 (SilcClientID *)client_id,
644 if (!client && server->server_type == SILC_ROUTER)
648 if (!client && server->standalone)
651 if (!client || !client->nickname || !client->username ||
653 SilcBuffer buffer, idp;
656 client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
657 client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
658 client->resolve_cmd_ident = ++server->cmd_ident;
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);