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 /* XXX TODO Requested Attributes to WHOIS
22 (NOTE: entry that is incomplete must be resolved first before resolving
25 #include "serverincludes.h"
26 #include "server_internal.h"
29 SilcSocketConnection sock; /* Connection of this query */
30 unsigned char **arg; /* Query argument */
31 SilcUInt32 *arg_lens; /* Query argument lengths */
32 SilcUInt32 *arg_types; /* Query argument types */
33 SilcUInt32 argc; /* Number of query arguments */
34 SilcUInt32 timeout; /* Max timeout for query to complete */
35 SilcUInt16 ident; /* Query command identifier */
36 } *SilcServerQueryList;
38 /* Represents an SILC ID */
41 SilcIdType id_type; /* ID type */
42 SilcUInt16 ident; /* Command identifier */
45 /* Represents one error occurred during query */
48 SilcUInt16 index; /* Index to IDs */
49 SilcIdType id_type; /* ID type */
50 unsigned int from_cmd : 1; /* TRUE if `index' is from command args,
51 otherwise from query->ids */
52 unsigned int error : 7; /* The actual error (SilcStatus) */
53 } *SilcServerQueryError;
55 /* Query session context */
58 char *nickname; /* Queried nickname */
59 char *nick_server; /* Queried nickname's server */
60 char *server_name; /* Queried server name */
61 char *channel_name; /* Queried channel name */
62 SilcServerQueryID ids; /* Queried IDs */
63 SilcUInt32 ids_count; /* number of queried IDs */
64 SilcUInt32 reply_count; /* Requested reply count */
65 SilcDList attrs; /* Requested Attributes in WHOIS */
67 /* Query session data */
68 SilcServerCommandContext cmd; /* Command context for query */
69 SilcServerQueryList querylist; /* Temporary query list context */
70 SilcServerQueryID queries; /* Ongoing queries */
71 SilcServerQueryError errors; /* Query errors */
72 SilcUInt16 querylist_count; /* Number of query lists */
73 SilcUInt16 queries_count; /* Number of ongoing queries */
74 SilcUInt16 queries_left; /* Number of ongoing queries left */
75 SilcUInt16 errors_count; /* number of errors */
76 unsigned int querycmd : 7; /* Query command (SilcCommand) */
77 unsigned int resolved : 1; /* TRUE if normal server has resolved
78 information from router */
81 void silc_server_query_free(SilcServerQuery query);
82 void silc_server_query_send_error(SilcServer server,
83 SilcServerQuery query,
84 SilcStatus error, ...);
85 void silc_server_query_add_error(SilcServer server,
86 SilcServerQuery query,
90 void silc_server_query_add_error_id(SilcServer server,
91 SilcServerQuery query,
93 void *id, SilcIdType id_type);
94 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
95 void silc_server_query_send_router_reply(void *context, void *reply);
96 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
97 void silc_server_query_process(SilcServer server, SilcServerQuery query,
99 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
100 SilcSocketConnection sock,
101 SilcClientEntry client_entry);
102 void silc_server_query_resolve_reply(void *context, void *reply);
103 void silc_server_query_send_reply(SilcServer server,
104 SilcServerQuery query,
105 SilcClientEntry *clients,
106 SilcUInt32 clients_count,
107 SilcServerEntry *servers,
108 SilcUInt32 servers_count,
109 SilcChannelEntry *channels,
110 SilcUInt32 channels_count);
112 /* Free the query context structure and all allocated resources. */
114 void silc_server_query_free(SilcServerQuery query)
118 silc_server_command_free(query->cmd);
120 for (i = 0; i < query->queries_count; i++)
121 silc_free(query->queries[i].id);
122 silc_free(query->queries);
124 silc_free(query->nickname);
125 silc_free(query->nick_server);
126 silc_free(query->server_name);
127 silc_free(query->channel_name);
129 for (i = 0; i < query->ids_count; i++)
130 silc_free(query->ids[i].id);
131 silc_free(query->ids);
134 silc_attribute_payload_list_free(query->attrs);
136 for (i = 0; i < query->errors_count; i++)
137 silc_free(query->errors[i].id);
138 silc_free(query->errors);
140 memset(query, 'F', sizeof(*query));
144 /* Send error reply indicated by the `error' to the original sender of
147 void silc_server_query_send_error(SilcServer server,
148 SilcServerQuery query,
149 SilcStatus error, ...)
152 unsigned char *data = NULL;
153 SilcUInt32 data_len = 0, data_type = 0, argc = 0;
156 data_type = va_arg(va, SilcUInt32);
159 data = va_arg(va, unsigned char *);
160 data_len = va_arg(va, SilcUInt32);
163 SILC_LOG_DEBUG(("ERROR: %s (%d)", silc_get_status_message(error), error));
165 /* Send the command reply with error */
166 silc_server_send_command_reply(server, query->cmd->sock,
167 query->querycmd, error, 0,
168 silc_command_get_ident(query->cmd->payload),
169 argc, data_type, data, data_len);
173 /* Add error to error list. Multiple errors may occur during the query
174 processing and this function can be used to add one error. The
175 `index' is the index to the command context which includes the argument
176 which caused the error, or it is the index to query->ids, depending
177 on value of `from_cmd'. */
179 void silc_server_query_add_error(SilcServer server,
180 SilcServerQuery query,
185 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
186 (query->errors_count + 1));
189 query->errors[query->errors_count].index = index;
190 query->errors[query->errors_count].from_cmd = from_cmd;
191 query->errors[query->errors_count].error = error;
192 query->errors[query->errors_count].id = NULL;
193 query->errors[query->errors_count].id_type = 0;
194 query->errors_count++;
197 /* Same as silc_server_query_add_error but adds the ID data to be used
198 with error sending with this error type. */
200 void silc_server_query_add_error_id(SilcServer server,
201 SilcServerQuery query,
203 void *id, SilcIdType id_type)
205 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
206 (query->errors_count + 1));
209 query->errors[query->errors_count].index = 0;
210 query->errors[query->errors_count].from_cmd = FALSE;
211 query->errors[query->errors_count].error = error;
212 query->errors[query->errors_count].id = silc_id_dup(id, id_type);
213 query->errors[query->errors_count].id_type = id_type;
214 query->errors_count++;
217 /* Processes query as command. The `query' is the command that is
218 being processed indicated by the `cmd'. The `query' can be one of
219 the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
220 SILC_COMMAND_IDENTIFY. This function handles the reply sending
221 to the entity who sent this query to us automatically. Returns
222 TRUE if the query is being processed or FALSE on error. */
224 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
225 SilcServerCommandContext cmd)
227 SilcServerQuery query;
229 SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd)));
231 query = silc_calloc(1, sizeof(*query));
232 query->querycmd = querycmd;
233 query->cmd = silc_server_command_dup(cmd);
237 case SILC_COMMAND_WHOIS:
238 /* If we are normal server and query contains nickname, send it
239 directly to router. */
240 if (server->server_type == SILC_SERVER && !server->standalone &&
241 cmd->sock != SILC_PRIMARY_ROUTE(server) &&
242 silc_argument_get_arg_type(cmd->args, 1, NULL)) {
243 silc_server_query_send_router(server, query);
248 case SILC_COMMAND_WHOWAS:
249 /* WHOWAS query is always sent to router if we are normal server */
250 if (server->server_type == SILC_SERVER && !server->standalone &&
251 cmd->sock != SILC_PRIMARY_ROUTE(server)) {
252 silc_server_query_send_router(server, query);
257 case SILC_COMMAND_IDENTIFY:
258 /* If we are normal server and query does not contain IDs, send it
259 directly to router (it contains nickname, server name or channel
261 if (server->server_type == SILC_SERVER && !server->standalone &&
262 cmd->sock != SILC_PRIMARY_ROUTE(server) &&
263 !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
264 silc_server_query_send_router(server, query);
270 SILC_LOG_ERROR(("Bad query using %d command", querycmd));
271 silc_server_query_free(query);
275 /* Now parse the request */
276 silc_server_query_parse(server, query);
281 /* Send the received query to our primary router since we could not
282 handle the query directly. We will reprocess the query after our
283 router replies back. */
285 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
288 SilcUInt16 old_ident;
290 SILC_LOG_DEBUG(("Forwarding the query to router for processing"));
292 /* Send WHOIS command to our router */
293 old_ident = silc_command_get_ident(query->cmd->payload);
294 silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
295 tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
296 silc_server_packet_send(server,
297 SILC_PRIMARY_ROUTE(server),
298 SILC_PACKET_COMMAND, 0,
299 tmpbuf->data, tmpbuf->len, TRUE);
300 silc_command_set_ident(query->cmd->payload, old_ident);
301 silc_buffer_free(tmpbuf);
303 query->resolved = TRUE;
305 /* Continue parsing the query after received reply from router */
306 silc_server_command_pending(server, query->querycmd, server->cmd_ident,
307 silc_server_query_send_router_reply, query);
310 /* Reply callback called after primary router has replied to our initial
311 sending of the query to it. We will proceed the query in this function. */
313 void silc_server_query_send_router_reply(void *context, void *reply)
315 SilcServerQuery query = context;
316 SilcServer server = query->cmd->server;
317 SilcServerCommandReplyContext cmdr = reply;
319 SILC_LOG_DEBUG(("Received reply from router to query"));
321 /* Check if router sent error reply */
322 if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
325 SILC_LOG_DEBUG(("Sending error to original query"));
327 /* Send the same command reply payload which contains the error */
328 silc_command_set_command(cmdr->payload, query->querycmd);
329 silc_command_set_ident(cmdr->payload,
330 silc_command_get_ident(query->cmd->payload));
331 buffer = silc_command_payload_encode_payload(cmdr->payload);
332 silc_server_packet_send(server, query->cmd->sock,
333 SILC_PACKET_COMMAND_REPLY, 0,
334 buffer->data, buffer->len, FALSE);
335 silc_buffer_free(buffer);
336 silc_server_query_free(query);
340 /* Continue with parsing */
341 silc_server_query_parse(server, query);
344 /* Parse the command query and start processing the queries in detail. */
346 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
348 SilcServerCommandContext cmd = query->cmd;
350 SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
355 SILC_LOG_DEBUG(("Parsing %s query",
356 silc_get_command_name(query->querycmd)));
358 switch (query->querycmd) {
360 case SILC_COMMAND_WHOIS:
361 /* Get Client IDs if present. Take IDs always instead of nickname. */
362 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
366 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
368 silc_server_query_send_error(server, query,
369 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
370 silc_server_query_free(query);
374 /* Get the nickname@server string and parse it */
375 if (!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);
383 /* Parse the IDs included in the query */
384 query->ids = silc_calloc(argc, sizeof(*query->ids));
386 for (i = 0; i < argc; i++) {
387 tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
391 id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
393 silc_server_query_add_error(server, query, TRUE, i + 4,
394 SILC_STATUS_ERR_BAD_CLIENT_ID);
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,
403 if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
404 !silc_idlist_find_client_by_id(server->global_list,
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);
415 query->ids[query->ids_count].id = id;
416 query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
421 /* Get the max count of reply messages allowed */
422 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
423 if (tmp && tmp_len == sizeof(SilcUInt32))
424 SILC_GET32_MSB(query->reply_count, tmp);
426 /* Get requested attributes if set */
427 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
429 query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
432 case SILC_COMMAND_WHOWAS:
434 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
436 silc_server_query_send_error(server, query,
437 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
438 silc_server_query_free(query);
442 /* Get the nickname@server string and parse it */
443 if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
444 silc_server_query_send_error(server, query,
445 SILC_STATUS_ERR_BAD_NICKNAME, 0);
446 silc_server_query_free(query);
450 /* Get the max count of reply messages allowed */
451 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
452 if (tmp && tmp_len == sizeof(SilcUInt32))
453 SILC_GET32_MSB(query->reply_count, tmp);
456 case SILC_COMMAND_IDENTIFY:
457 /* Get IDs if present. Take IDs always instead of names. */
458 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
461 /* Try get nickname */
462 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
464 /* Get the nickname@server string and parse it */
465 if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
466 silc_server_query_add_error(server, query, TRUE, 1,
467 SILC_STATUS_ERR_BAD_NICKNAME);
470 /* Try get server name */
471 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
473 query->server_name = silc_memdup(tmp, tmp_len);
475 /* Get channel name */
476 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
478 query->channel_name = silc_memdup(tmp, tmp_len);
481 /* Parse the IDs included in the query */
482 query->ids = silc_calloc(argc, sizeof(*query->ids));
484 for (i = 0; i < argc; i++) {
485 tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
489 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
491 silc_server_query_add_error(server, query, TRUE, i + 5,
492 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
496 /* Normal server must check whether this ID exist, and if not then
497 send the query to router, unless done so already */
498 if (server->server_type == SILC_SERVER && !query->resolved) {
499 if (!silc_idlist_find_client_by_id(server->local_list,
501 if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT ||
502 !silc_idlist_find_client_by_id(server->global_list,
504 silc_server_query_send_router(server, query);
505 for (i = 0; i < query->ids_count; i++)
506 silc_free(query->ids[i].id);
507 silc_free(query->ids);
513 query->ids[query->ids_count].id = id;
514 query->ids[query->ids_count].id_type = id_type;
519 /* Get the max count of reply messages allowed */
520 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
521 if (tmp && tmp_len == sizeof(SilcUInt32))
522 SILC_GET32_MSB(query->reply_count, tmp);
526 /* Start processing the query information */
527 silc_server_query_process(server, query, TRUE);
530 /* Processes the parsed query. This does the actual finding of the
531 queried information and prepares for sending reply to the original
532 sender of the query command. */
534 void silc_server_query_process(SilcServer server, SilcServerQuery query,
537 SilcServerCommandContext cmd = query->cmd;
538 bool check_global = FALSE;
540 SilcClientEntry *clients = NULL, client_entry;
541 SilcChannelEntry *channels = NULL;
542 SilcServerEntry *servers = NULL;
543 SilcUInt32 clients_count = 0, channels_count = 0, servers_count = 0;
546 SILC_LOG_DEBUG(("Processing %s query",
547 silc_get_command_name(query->querycmd)));
549 /* Check global lists if query is coming from client or we are not
550 normal server (we know global information). */
551 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
553 else if (server->server_type != SILC_SERVER)
556 if (query->nickname) {
557 /* Get all clients matching nickname from local list */
558 if (!silc_idlist_get_clients_by_hash(server->local_list,
559 query->nickname, server->md5hash,
560 &clients, &clients_count))
561 silc_idlist_get_clients_by_nickname(server->local_list,
564 &clients, &clients_count);
566 /* Check global list as well */
568 if (!silc_idlist_get_clients_by_hash(server->global_list,
569 query->nickname, server->md5hash,
570 &clients, &clients_count))
571 silc_idlist_get_clients_by_nickname(server->global_list,
574 &clients, &clients_count);
578 silc_server_query_add_error(server, query, TRUE, 1,
579 SILC_STATUS_ERR_NO_SUCH_NICK);
582 if (query->server_name) {
583 /* Find server by name */
584 entry = silc_idlist_find_server_by_name(server->local_list,
585 query->server_name, TRUE, NULL);
586 if (!entry && check_global)
587 entry = silc_idlist_find_server_by_name(server->global_list,
588 query->server_name, TRUE, NULL);
590 servers = silc_realloc(servers, sizeof(*servers) * (servers_count + 1));
591 servers[servers_count++] = (SilcServerEntry)entry;
595 silc_server_query_add_error(server, query, TRUE, 2,
596 SILC_STATUS_ERR_NO_SUCH_SERVER);
599 if (query->channel_name) {
600 /* Find channel by name */
601 entry = silc_idlist_find_channel_by_name(server->local_list,
602 query->channel_name, NULL);
603 if (!entry && check_global)
604 entry = silc_idlist_find_channel_by_name(server->global_list,
605 query->channel_name, NULL);
607 channels = silc_realloc(channels, sizeof(*channels) *
608 (channels_count + 1));
609 channels[channels_count++] = (SilcChannelEntry)entry;
613 silc_server_query_add_error(server, query, TRUE, 3,
614 SILC_STATUS_ERR_NO_SUCH_CHANNEL);
617 if (query->ids_count) {
618 /* Find entries by the queried IDs */
619 for (i = 0; i < query->ids_count; i++) {
620 void *id = query->ids[i].id;
624 switch (query->ids[i].id_type) {
627 /* Get client entry */
628 entry = silc_idlist_find_client_by_id(server->local_list,
630 if (!entry && check_global)
631 entry = silc_idlist_find_client_by_id(server->global_list,
634 silc_server_query_add_error(server, query, FALSE, i,
635 SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
639 clients = silc_realloc(clients, sizeof(*clients) *
640 (clients_count + 1));
641 clients[clients_count++] = (SilcClientEntry)entry;
645 /* Get server entry */
646 entry = silc_idlist_find_server_by_id(server->local_list,
648 if (!entry && check_global)
649 entry = silc_idlist_find_server_by_id(server->global_list,
652 silc_server_query_add_error(server, query, FALSE, i,
653 SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
657 servers = silc_realloc(servers, sizeof(*servers) *
658 (servers_count + 1));
659 servers[servers_count++] = (SilcServerEntry)entry;
662 case SILC_ID_CHANNEL:
663 /* Get channel entry */
664 entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
665 if (!entry && check_global)
666 entry = silc_idlist_find_channel_by_id(server->global_list, id,
669 silc_server_query_add_error(server, query, FALSE, i,
670 SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID);
674 channels = silc_realloc(channels, sizeof(*channels) *
675 (channels_count + 1));
676 channels[channels_count++] = (SilcChannelEntry)entry;
685 /* If nothing was found, then just send the errors */
686 if (!clients && !channels && !servers) {
687 silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
691 /* If caller does not want us to resolve anything (has resolved already)
692 then just continue with sending the reply */
694 silc_server_query_send_reply(server, query, clients, clients_count,
695 servers, servers_count, channels,
703 /* Now process all found information and if necessary do some more
705 switch (query->querycmd) {
707 case SILC_COMMAND_WHOIS:
708 for (i = 0; i < clients_count; i++) {
709 client_entry = clients[i];
711 /* Check if cannot query this anyway, so take next one */
713 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
716 /* If requested attributes is set then we always resolve the client
717 information, if not then check whether the entry is complete or not
718 and decide whether we need to resolve or not. */
720 if (client_entry->nickname && client_entry->username &&
721 client_entry->userinfo) {
722 /* Check if cannot query this anyway, so take next one */
723 if (!client_entry->router)
726 /* If we are router, client is local to us, or client is on channel
727 we do not need to resolve the client information. */
728 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
729 || silc_hash_table_count(client_entry->channels) ||
735 /* When requested attributes is present and local client is detached
736 we cannot send the command to the client, we'll reply on behalf of
737 the client instead. */
738 if (query->attrs && SILC_IS_LOCAL(client_entry) &&
739 (client_entry->mode & SILC_UMODE_DETACHED ||
740 client_entry->data.status & SILC_IDLIST_STATUS_NOATTR))
743 /* Resolve the detailed client information. If client is local we
744 know that attributes were present and we will resolve directly
745 from the client. Otherwise resolve from client's owner. */
746 silc_server_query_resolve(server, query,
747 (SILC_IS_LOCAL(client_entry) ?
748 client_entry->connection :
749 client_entry->router->connection),
754 case SILC_COMMAND_WHOWAS:
755 for (i = 0; i < clients_count; i++) {
756 client_entry = clients[i];
758 /* Check if cannot query this anyway, so take next one */
759 if (!client_entry || !client_entry->router ||
760 client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
763 /* If both nickname and username are present no resolving is needed */
764 if (client_entry->nickname && client_entry->username)
767 /* Resolve the detailed client information */
768 silc_server_query_resolve(server, query,
769 client_entry->router->connection,
774 case SILC_COMMAND_IDENTIFY:
775 for (i = 0; i < clients_count; i++) {
776 client_entry = clients[i];
778 /* Check if cannot query this anyway, so take next one */
779 if (!client_entry || !client_entry->router ||
780 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED))
783 if (client_entry->nickname) {
784 /* If we are router, client is local to us, or client is on channel
785 we do not need to resolve the client information. */
786 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
787 || silc_hash_table_count(client_entry->channels) ||
792 /* Resolve the detailed client information */
793 silc_server_query_resolve(server, query,
794 client_entry->router->connection,
800 if (!query->queries_count)
801 /* If we didn't have to do any resolving, continue with sending the
802 command reply to the original sender. */
803 silc_server_query_send_reply(server, query, clients, clients_count,
804 servers, servers_count, channels,
807 /* Now actually send the resolvings we gathered earlier */
808 silc_server_query_resolve(server, query, NULL, NULL);
815 /* Resolve the detailed information for the `client_entry'. Only client
816 information needs to be resolved for being incomplete. Each incomplete
817 client entry calls this function to do the resolving. */
819 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
820 SilcSocketConnection sock,
821 SilcClientEntry client_entry)
823 SilcServerCommandContext cmd = query->cmd;
824 SilcServerQueryList r = NULL;
831 if (!sock && client_entry)
834 /* If arguments are NULL we will now actually send the resolvings
835 that earlier has been gathered by calling this function. */
836 if (!sock && !client_entry) {
839 SILC_LOG_DEBUG(("Sending the resolvings"));
841 /* WHOWAS resolving has been done at the same time this function
842 was called to add the resolving for WHOWAS, so just return. */
843 if (query->querycmd == SILC_COMMAND_WHOWAS)
846 for (i = 0; i < query->querylist_count; i++) {
847 r = &query->querylist[i];
849 /* Send WHOIS command */
850 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
851 r->argc, r->arg, r->arg_lens,
852 r->arg_types, r->ident);
853 silc_server_packet_send(server, r->sock, SILC_PACKET_COMMAND, 0,
854 res_cmd->data, res_cmd->len, FALSE);
855 silc_buffer_free(res_cmd);
857 /* Reprocess this packet after received reply */
858 if (silc_server_command_pending_timed(server, SILC_COMMAND_WHOIS,
860 silc_server_query_resolve_reply,
862 query->queries_left++;
865 /* Cleanup this temporary context */
866 for (i = 0; i < query->querylist_count; i++) {
868 for (k = 0; k < query->querylist[i].argc; k++)
869 silc_free(query->querylist[i].arg[k]);
870 silc_free(query->querylist[i].arg);
871 silc_free(query->querylist[i].arg_lens);
872 silc_free(query->querylist[i].arg_types);
874 silc_free(query->querylist);
875 query->querylist = NULL;
876 query->querylist_count = 0;
880 SILC_LOG_DEBUG(("Resolving client information"));
882 if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
883 /* The entry is being resolved by some other external query already.
884 Attach to that query instead of resolving again. */
885 ident = client_entry->resolve_cmd_ident;
886 if (silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
887 silc_server_query_resolve_reply, query))
888 query->queries_left++;
890 /* This entry will be resolved */
891 ident = ++server->cmd_ident;
893 switch (query->querycmd) {
895 case SILC_COMMAND_WHOIS:
896 case SILC_COMMAND_IDENTIFY:
897 /* Take existing query context if exist for this connection */
898 for (i = 0; i < query->queries_count; i++)
899 if (query->querylist[i].sock == sock) {
900 r = &query->querylist[i];
905 /* Allocate new temp query list context */
906 query->querylist = silc_realloc(query->querylist,
907 sizeof(*query->querylist) *
908 (query->querylist_count + 1));
909 r = &query->querylist[query->querylist_count];
910 query->querylist_count++;
911 memset(r, 0, sizeof(*r));
914 if (SILC_IS_LOCAL(client_entry))
918 /* If attributes were present put them to this resolving */
919 if (query->attrs && query->querycmd == SILC_COMMAND_WHOIS) {
921 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
922 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
923 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
925 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
927 r->arg[r->argc] = silc_memdup(tmp, len);
928 r->arg_lens[r->argc] = len;
929 r->arg_types[r->argc] = 3;
934 r->arg = silc_realloc(r->arg, sizeof(*r->arg) * len);
935 r->arg_lens = silc_realloc(r->arg_lens, sizeof(*r->arg_lens) * len);
936 r->arg_types = silc_realloc(r->arg_types, sizeof(*r->arg_types) * len);
938 /* Add the client entry to be resolved */
939 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
940 r->arg[r->argc] = silc_memdup(idp->data, idp->len);
941 r->arg_lens[r->argc] = idp->len;
942 r->arg_types[r->argc] = r->argc + 4;
944 silc_buffer_free(idp);
948 case SILC_COMMAND_WHOWAS:
949 /* We must send WHOWAS command since it's the only the way of
950 resolving clients that are not present in the network anymore. */
951 silc_server_send_command(server, sock, query->querycmd, ident, 1,
952 1, query->nickname, strlen(query->nickname));
953 if (silc_server_command_pending(server, query->querycmd, ident,
954 silc_server_query_resolve_reply, query))
955 query->queries_left++;
960 /* Mark the entry as being resolved */
961 client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
962 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
963 client_entry->resolve_cmd_ident = ident;
965 /* Save the queried ID, which we will reprocess after we get this and
966 all other queries back. */
967 query->queries = silc_realloc(query->queries, sizeof(*query->queries) *
968 (query->queries_count + 1));
969 if (query->queries) {
970 i = query->queries_count;
971 query->queries[i].id = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
972 query->queries[i].id_type = SILC_ID_CLIENT;
973 query->queries[i].ident = ident;
974 query->queries_count++;
978 /* Reply callback called after one resolving has been completed. If
979 all resolvings has been received then we will continue with sending
980 the command reply to the original sender of the query. */
982 void silc_server_query_resolve_reply(void *context, void *reply)
984 SilcServerQuery query = context;
985 SilcServer server = query->cmd->server;
986 SilcServerCommandReplyContext cmdr = reply;
987 SilcUInt16 ident = cmdr->ident;
988 SilcStatus error = SILC_STATUS_OK;
989 SilcServerQueryID id = NULL;
990 SilcClientEntry client_entry;
993 /* One less query left */
994 query->queries_left--;
996 silc_command_get_status(cmdr->payload, NULL, &error);
997 SILC_LOG_DEBUG(("Received reply to resolving (%d left) (status=%d)",
998 query->queries_left, error));
1000 /* If no error then skip to other stuff */
1001 if (error == SILC_STATUS_OK)
1004 /* Error occurred during resolving */
1006 /* Find the resolved client ID */
1007 for (i = 0; i < query->queries_count; i++) {
1008 if (query->queries[i].ident != ident)
1011 id = &query->queries[i];
1013 if (error == SILC_STATUS_ERR_TIMEDOUT) {
1014 /* If timeout occurred for local entry when resolving attributes
1015 mark that this client doesn't support attributes in WHOIS. This
1016 assures we won't send the request again to the client. */
1017 if (query->querycmd == SILC_COMMAND_WHOIS && query->attrs) {
1018 client_entry = silc_idlist_find_client_by_id(server->local_list,
1019 id->id, TRUE, NULL);
1020 SILC_LOG_DEBUG(("Client %s does not support Requested Attributes",
1021 silc_id_render(id->id, SILC_ID_CLIENT)));
1022 if (client_entry && SILC_IS_LOCAL(client_entry))
1023 client_entry->data.status |= SILC_IDLIST_STATUS_NOATTR;
1030 /* If there are queries left then wait for them */
1031 if (query->queries_left)
1034 SILC_LOG_DEBUG(("Reprocess the query"));
1036 /* We have received all queries. Now re-search all information required
1037 to complete this query. Reason we cannot save the values found in
1038 the first search is that SilcClientEntry, SilcServerEntry and
1039 SilcChannelEntry pointers may become invalid while we were waiting
1040 for these resolvings. */
1041 silc_server_query_process(server, query, FALSE);
1044 /* Send the reply to the original query. If arguments are NULL then this
1045 sends only the errors that has occurred during the processing of the
1046 query. This sends the errors always after sending all the found
1047 information. The query is over after this function returns and the
1048 `query' will become invalid. This is called only after all informations
1049 has been resolved. This means that if something is not found or is
1050 incomplete in this function we were unable to resolve the information
1051 or it does not exist at all. */
1053 void silc_server_query_send_reply(SilcServer server,
1054 SilcServerQuery query,
1055 SilcClientEntry *clients,
1056 SilcUInt32 clients_count,
1057 SilcServerEntry *servers,
1058 SilcUInt32 servers_count,
1059 SilcChannelEntry *channels,
1060 SilcUInt32 channels_count)
1062 SilcServerCommandContext cmd = query->cmd;
1063 SilcUInt16 ident = silc_command_get_ident(cmd->payload);
1068 int i, k, valid_count;
1069 char nh[256], uh[256];
1071 SILC_LOG_DEBUG(("Sending reply to query"));
1073 status = SILC_STATUS_OK;
1076 if (clients_count) {
1077 SilcClientEntry entry;
1078 SilcSocketConnection hsock;
1080 /* Mark all invalid entries */
1081 for (i = 0, valid_count = 0; i < clients_count; i++) {
1083 switch (query->querycmd) {
1084 case SILC_COMMAND_WHOIS:
1085 if (!entry->nickname || !entry->username || !entry->userinfo ||
1086 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1087 /* When querying by ID, every "unfound" entry must cause error */
1089 silc_server_query_add_error_id(server, query,
1090 SILC_STATUS_ERR_TIMEDOUT,
1091 entry->id, SILC_ID_CLIENT);
1097 case SILC_COMMAND_IDENTIFY:
1098 if (!entry->nickname ||
1099 !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
1100 /* When querying by ID, every "unfound" entry must cause error */
1102 silc_server_query_add_error_id(server, query,
1103 SILC_STATUS_ERR_TIMEDOUT,
1104 entry->id, SILC_ID_CLIENT);
1110 case SILC_COMMAND_WHOWAS:
1111 if (!entry->nickname || !entry->username ||
1112 entry->data.status & SILC_IDLIST_STATUS_REGISTERED) {
1121 /* Start processing found clients */
1122 status = SILC_STATUS_OK;
1123 if (valid_count > 1)
1124 status = SILC_STATUS_LIST_START;
1126 /* Now do the sending of valid entries */
1128 for (i = 0; i < clients_count && valid_count; i++) {
1134 status = SILC_STATUS_LIST_ITEM;
1135 if (valid_count > 1 && k == valid_count - 1
1136 && !servers_count && !channels_count && !query->errors_count)
1137 status = SILC_STATUS_LIST_END;
1138 if (query->reply_count && k - 1 == query->reply_count)
1139 status = SILC_STATUS_LIST_END;
1141 SILC_LOG_DEBUG(("%s: client %s",
1142 (status == SILC_STATUS_OK ? " OK" :
1143 status == SILC_STATUS_LIST_START ? "START" :
1144 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1145 status == SILC_STATUS_LIST_END ? " END" :
1146 " : "), entry->nickname));
1148 idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
1149 memset(uh, 0, sizeof(uh));
1150 memset(nh, 0, sizeof(nh));
1152 silc_strncat(nh, sizeof(nh), entry->nickname, strlen(entry->nickname));
1153 if (!strchr(entry->nickname, '@')) {
1154 silc_strncat(nh, sizeof(nh), "@", 1);
1155 if (entry->servername) {
1156 silc_strncat(nh, sizeof(nh), entry->servername,
1157 strlen(entry->servername));
1159 len = entry->router ? strlen(entry->router->server_name) :
1160 strlen(server->server_name);
1161 silc_strncat(nh, sizeof(nh), entry->router ?
1162 entry->router->server_name :
1163 server->server_name, len);
1167 switch (query->querycmd) {
1169 case SILC_COMMAND_WHOIS:
1171 unsigned char idle[4], mode[4];
1172 unsigned char *fingerprint, fempty[20];
1173 SilcBuffer channels, umode_list = NULL;
1175 memset(fempty, 0, sizeof(fempty));
1176 silc_strncat(uh, sizeof(uh), entry->username,
1177 strlen(entry->username));
1178 if (!strchr(entry->username, '@') && entry->connection) {
1179 hsock = entry->connection;
1180 silc_strncat(uh, sizeof(uh), "@", 1);
1181 len = strlen(hsock->hostname);
1182 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1185 if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
1187 silc_server_get_client_channel_list(server, entry, FALSE,
1188 FALSE, &umode_list);
1191 silc_server_get_client_channel_list(server, entry, TRUE,
1194 if (memcmp(entry->data.fingerprint, fempty, sizeof(fempty)))
1195 fingerprint = entry->data.fingerprint;
1199 SILC_PUT32_MSB(entry->mode, mode);
1200 if (entry->connection)
1201 SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
1203 /* Send command reply */
1204 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1205 status, 0, ident, 9,
1206 2, idp->data, idp->len,
1210 strlen(entry->userinfo),
1211 6, channels ? channels->data : NULL,
1212 channels ? channels->len : 0,
1216 fingerprint ? 20 : 0,
1217 10, umode_list ? umode_list->data :
1218 NULL, umode_list ? umode_list->len :
1222 silc_buffer_free(channels);
1224 silc_buffer_free(umode_list);
1230 case SILC_COMMAND_IDENTIFY:
1231 if (!entry->username) {
1232 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1233 status, 0, ident, 2,
1234 2, idp->data, idp->len,
1237 silc_strncat(uh, sizeof(uh), entry->username,
1238 strlen(entry->username));
1239 if (!strchr(entry->username, '@') && entry->connection) {
1240 hsock = entry->connection;
1241 silc_strncat(uh, sizeof(uh), "@", 1);
1242 len = strlen(hsock->hostname);
1243 silc_strncat(uh, sizeof(uh), hsock->hostname, len);
1246 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1247 status, 0, ident, 3,
1248 2, idp->data, idp->len,
1254 case SILC_COMMAND_WHOWAS:
1255 silc_strncat(uh, sizeof(uh), entry->username, strlen(entry->username));
1256 if (!strchr(entry->username, '@'))
1257 silc_strncat(uh, sizeof(uh), "@*private*", 10);
1259 /* Send command reply */
1260 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1261 status, 0, ident, 4,
1262 2, idp->data, idp->len,
1267 strlen(entry->userinfo) : 0);
1271 silc_buffer_free(idp);
1273 if (status == SILC_STATUS_LIST_END)
1279 /* Not one valid entry was found, send error. If nickname was used
1280 in query send error based on that, otherwise the query->errors
1281 already includes proper errors. */
1282 if (query->nickname)
1283 silc_server_query_add_error(server, query, TRUE, 1,
1284 SILC_STATUS_ERR_NO_SUCH_NICK);
1289 if (query->querycmd == SILC_COMMAND_IDENTIFY && servers_count) {
1290 SilcServerEntry entry;
1292 if (status == SILC_STATUS_OK && servers_count > 1)
1293 status = SILC_STATUS_LIST_START;
1296 for (i = 0; i < servers_count; i++) {
1300 status = SILC_STATUS_LIST_ITEM;
1301 if (servers_count > 1 && k == servers_count - 1 && !channels_count &&
1302 !query->errors_count)
1303 status = SILC_STATUS_LIST_END;
1304 if (query->reply_count && k - 1 == query->reply_count)
1305 status = SILC_STATUS_LIST_END;
1307 SILC_LOG_DEBUG(("%s: server %s",
1308 (status == SILC_STATUS_OK ? " OK" :
1309 status == SILC_STATUS_LIST_START ? "START" :
1310 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1311 status == SILC_STATUS_LIST_END ? " END" :
1313 entry->server_name ? entry->server_name : ""));
1315 /* Send command reply */
1316 idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
1317 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1318 status, 0, ident, 2,
1319 2, idp->data, idp->len,
1320 3, entry->server_name,
1321 entry->server_name ?
1322 strlen(entry->server_name) : 0);
1323 silc_buffer_free(idp);
1325 if (status == SILC_STATUS_LIST_END)
1332 if (query->querycmd == SILC_COMMAND_IDENTIFY && channels_count) {
1333 SilcChannelEntry entry;
1335 if (status == SILC_STATUS_OK && channels_count > 1)
1336 status = SILC_STATUS_LIST_START;
1339 for (i = 0; i < channels_count; i++) {
1340 entry = channels[i];
1343 status = SILC_STATUS_LIST_ITEM;
1344 if (channels_count > 1 && k == channels_count - 1 &&
1345 !query->errors_count)
1346 status = SILC_STATUS_LIST_END;
1347 if (query->reply_count && k - 1 == query->reply_count)
1348 status = SILC_STATUS_LIST_END;
1350 SILC_LOG_DEBUG(("%s: channel %s",
1351 (status == SILC_STATUS_OK ? " OK" :
1352 status == SILC_STATUS_LIST_START ? "START" :
1353 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1354 status == SILC_STATUS_LIST_END ? " END" :
1356 entry->channel_name ? entry->channel_name : ""));
1358 /* Send command reply */
1359 idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
1360 silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_IDENTIFY,
1361 status, 0, ident, 2,
1362 2, idp->data, idp->len,
1363 3, entry->channel_name,
1364 entry->channel_name ?
1365 strlen(entry->channel_name) : 0);
1366 silc_buffer_free(idp);
1368 if (status == SILC_STATUS_LIST_END)
1375 if (query->errors_count) {
1378 if (status == SILC_STATUS_OK && query->errors_count > 1)
1379 status = SILC_STATUS_LIST_START;
1382 for (i = 0; i < query->errors_count; i++) {
1385 /* Take error argument */
1386 if (query->errors[i].from_cmd) {
1387 tmp = silc_argument_get_arg_type(cmd->args,
1388 query->errors[i].index, &len);
1389 if (query->errors[i].index == 1)
1390 type = 3; /* Nickname */
1393 } else if (!query->errors[i].id) {
1395 silc_id_payload_encode(query->ids[query->errors[i].index].id,
1396 query->ids[query->errors[k].index].id_type);
1401 idp = silc_id_payload_encode(query->errors[i].id,
1402 query->errors[k].id_type);
1409 status = SILC_STATUS_LIST_ITEM;
1410 if (query->errors_count > 1 && k == query->errors_count - 1)
1411 status = SILC_STATUS_LIST_END;
1412 if (query->reply_count && k - 1 == query->reply_count)
1413 status = SILC_STATUS_LIST_END;
1415 SILC_LOG_DEBUG(("%s: ERROR: %s (%d)",
1416 (status == SILC_STATUS_OK ? " OK" :
1417 status == SILC_STATUS_LIST_START ? "START" :
1418 status == SILC_STATUS_LIST_ITEM ? " ITEM" :
1419 status == SILC_STATUS_LIST_END ? " END" :
1421 silc_get_status_message(query->errors[i].error),
1422 query->errors[i].error));
1425 silc_server_send_command_reply(server, cmd->sock, query->querycmd,
1426 (status == SILC_STATUS_OK ?
1427 query->errors[i].error : status),
1428 (status == SILC_STATUS_OK ?
1429 0 : query->errors[i].error), ident, 1,
1431 silc_buffer_free(idp);
1433 if (status == SILC_STATUS_LIST_END)
1440 silc_server_query_free(query);
1443 /* Find client by the Client ID indicated by the `client_id', and if not
1444 found then query it by using WHOIS command. The client information
1445 is also resolved if the cached information is incomplete or if the
1446 `always_resolve' is set to TRUE. The indication whether requested
1447 client was being resolved is saved into `resolved'. If the client
1448 is not being resolved its entry is returned by this function. NULL
1449 is returned if client is resolved. */
1451 SilcClientEntry silc_server_query_client(SilcServer server,
1452 const SilcClientID *client_id,
1453 bool always_resolve,
1456 SilcClientEntry client;
1458 SILC_LOG_DEBUG(("Resolving client by client ID"));
1463 client = silc_idlist_find_client_by_id(server->local_list,
1464 (SilcClientID *)client_id,
1467 client = silc_idlist_find_client_by_id(server->global_list,
1468 (SilcClientID *)client_id,
1470 if (!client && server->server_type == SILC_ROUTER)
1474 if (!client && server->standalone)
1477 if (!client || !client->nickname || !client->username ||
1479 SilcBuffer buffer, idp;
1482 client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
1483 client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
1484 client->resolve_cmd_ident = ++server->cmd_ident;
1487 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
1488 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
1489 server->cmd_ident, 1,
1490 4, idp->data, idp->len);
1491 silc_server_packet_send(server, client ? client->router->connection :
1492 SILC_PRIMARY_ROUTE(server),
1493 SILC_PACKET_COMMAND, 0,
1494 buffer->data, buffer->len, FALSE);
1495 silc_buffer_free(idp);
1496 silc_buffer_free(buffer);