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"
24 /* Represents an SILC ID */
27 SilcIdType id_type; /* ID type */
30 /* Represents one error occurred during query */
32 SilcUInt32 index; /* Index to IDs */
33 bool from_cmd; /* TRUE if `index' is from command args,
34 otherwise from query->ids */
35 SilcStatus error; /* The actual error */
36 } *SilcServerQueryError;
38 /* Context for ongoing queries that server the original query (like to
39 resolve detailed information from some other server before replying
40 to the original sender). */
42 unsigned char **arg; /* Query argument */
43 SilcUInt32 *arg_lens; /* Query argument lengths */
44 SilcUInt32 *arg_types; /* Query argument types */
45 SilcUInt32 argc; /* Number of query arguments */
46 SilcUInt32 timeout; /* Max timeout for query to complete */
47 SilcUInt16 ident; /* Query command identifier */
48 } *SilcServerQueryList;
50 /* Query session context */
52 /* Query session data */
53 SilcCommand querycmd; /* Query command */
54 SilcServerCommandContext cmd; /* Command context for query */
55 SilcServerQueryList queries; /* Ongoing queries */
56 SilcUInt32 num_query; /* Number of ongoing queries left */
59 char *nickname; /* Queried nickname */
60 char *nick_server; /* Queried nickname's server */
61 char *server_name; /* Queried server name */
62 char *channel_name; /* Queried channel name */
63 SilcServerQueryID ids; /* Queried IDs */
64 SilcUInt32 ids_count; /* number of queried IDs */
65 SilcUInt32 reply_count; /* Requested reply count */
66 SilcDList attrs; /* Requested Attributes in WHOIS */
68 SilcServerQueryError errors; /* Query errors */
69 SilcUInt32 errors_count; /* number of errors */
72 void silc_server_query_free(SilcServerQuery query);
73 bool silc_server_query_check_error(SilcServer server,
74 SilcServerQuery query,
75 SilcServerCommandReplyContext cmdr);
76 void silc_server_query_send_error(SilcServer server,
77 SilcServerQuery query,
78 SilcStatus error, ...);
79 void silc_server_query_add_error(SilcServer server,
80 SilcServerQuery query,
84 void silc_server_query_send_router(SilcServer server, SilcServerQuery query);
85 void silc_server_query_send_router_reply(void *context, void *reply);
86 void silc_server_query_parse(SilcServer server, SilcServerQuery query);
87 void silc_server_query_process(SilcServer server, SilcServerQuery query);
88 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
89 SilcSocketConnection sock,
90 SilcClientEntry client_entry);
91 void silc_server_query_resolve_reply(void *context, void *reply);
92 void silc_server_query_send_reply(SilcServer server,
93 SilcServerQuery query,
94 SilcClientEntry *clients,
95 SilcUInt32 clients_count,
96 SilcServerEntry *servers,
97 SilcUInt32 servers_count,
98 SilcChannelEntry *channels,
99 SilcUInt32 channels_count);
101 /* Free the query context structure and all allocated resources. */
103 void silc_server_query_free(SilcServerQuery query)
107 silc_server_command_free(query->cmd);
109 silc_free(query->nickname);
110 silc_free(query->nick_server);
111 silc_free(query->server_name);
112 silc_free(query->channel_name);
114 for (i = 0; i < query->ids_count; i++)
115 silc_free(query->ids[i].id);
116 silc_free(query->ids);
119 silc_attribute_payload_list_free(query->attrs);
121 silc_free(query->errors);
123 memset(query, 'F', sizeof(*query));
127 /* Check whether command reply contained error, and reply the error to
128 the original sender if it occurred. */
130 bool silc_server_query_check_error(SilcServer server,
131 SilcServerQuery query,
132 SilcServerCommandReplyContext cmdr)
137 if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
140 /* Send the same command reply payload which contains the error */
141 silc_command_set_command(cmdr->payload, query->querycmd);
142 silc_command_set_ident(cmdr->payload,
143 silc_command_get_ident(query->cmd->payload));
144 buffer = silc_command_payload_encode_payload(cmdr->payload);
145 silc_server_packet_send(server, query->cmd->sock,
146 SILC_PACKET_COMMAND_REPLY, 0,
147 buffer->data, buffer->len, FALSE);
148 silc_buffer_free(buffer);
155 /* Send error reply indicated by the `error' to the original sender of
158 void silc_server_query_send_error(SilcServer server,
159 SilcServerQuery query,
160 SilcStatus error, ...)
163 unsigned char *data = NULL;
164 SilcUInt32 data_len = 0, data_type = 0, argc = 0;
167 data_type = va_arg(va, SilcUInt32);
170 data = va_arg(va, unsigned char *);
171 data_len = va_arg(va, SilcUInt32);
174 /* Send the command reply with error */
175 silc_server_send_command_reply(server, query->cmd->sock,
176 query->querycmd, error, 0,
177 silc_command_get_ident(query->cmd->payload),
178 argc, data_type, data, data_len);
182 /* Add error to error list. Multiple errors may occur during the query
183 processing and this function can be used to add one error. The
184 `index' is the index to the command context which includes the argument
185 which caused the error, or it is the index to query->ids, depending
186 on value of `from_cmd'. */
188 void silc_server_query_add_error(SilcServer server,
189 SilcServerQuery query,
194 query->errors = silc_realloc(query->errors, sizeof(*query->errors) *
195 (query->errors_count + 1));
198 query->errors[query->errors_count].index = index;
199 query->errors[query->errors_count].from_cmd = from_cmd;
200 query->errors[query->errors_count].error = error;
201 query->errors_count++;
204 /* Processes query as command. The `query' is the command that is
205 being processed indicated by the `cmd'. The `query' can be one of
206 the following: SILC_COMMAND_WHOIS, SILC_COMMAND_WHOWAS or
207 SILC_COMMAND_IDENTIFY. This function handles the reply sending
208 to the entity who sent this query to us automatically. Returns
209 TRUE if the query is being processed or FALSE on error. */
211 bool silc_server_query_command(SilcServer server, SilcCommand querycmd,
212 SilcServerCommandContext cmd)
214 SilcServerQuery query;
216 query = silc_calloc(1, sizeof(*query));
217 query->querycmd = querycmd;
218 query->cmd = silc_server_command_dup(cmd);
222 case SILC_COMMAND_WHOIS:
223 /* If we are normal server and query contains nickname, send it
224 directly to router. */
225 if (server->server_type == SILC_SERVER && !server->standalone &&
226 silc_argument_get_arg_type(cmd->args, 1, NULL)) {
227 silc_server_query_send_router(server, query);
232 case SILC_COMMAND_WHOWAS:
233 /* WHOWAS query is always sent to router if we are normal server */
234 if (server->server_type == SILC_SERVER && !server->standalone) {
235 silc_server_query_send_router(server, query);
240 case SILC_COMMAND_IDENTIFY:
241 /* If we are normal server and query does not contain IDs, send it
242 directly to router (it contains nickname, server name or channel
244 if (server->server_type == SILC_SERVER && !server->standalone &&
245 !silc_argument_get_arg_type(cmd->args, 5, NULL)) {
246 silc_server_query_send_router(server, query);
252 SILC_LOG_ERROR(("Bad query using %d command", querycmd));
253 silc_server_query_free(query);
257 /* Now parse the request */
258 silc_server_query_parse(server, query);
263 /* Send the received query to our primary router since we could not
264 handle the query directly. We will reprocess the query after our
265 router replies back. */
267 void silc_server_query_send_router(SilcServer server, SilcServerQuery query)
270 SilcUInt16 old_ident;
272 /* Send WHOIS command to our router */
273 old_ident = silc_command_get_ident(query->cmd->payload);
274 silc_command_set_ident(query->cmd->payload, ++server->cmd_ident);
275 tmpbuf = silc_command_payload_encode_payload(query->cmd->payload);
276 silc_server_packet_send(server,
277 SILC_PRIMARY_ROUTE(server),
278 SILC_PACKET_COMMAND, 0,
279 tmpbuf->data, tmpbuf->len, TRUE);
280 silc_command_set_ident(query->cmd->payload, old_ident);
281 silc_buffer_free(tmpbuf);
283 /* Continue parsing the query after received reply from router */
284 silc_server_command_pending(server, query->querycmd, server->cmd_ident,
285 silc_server_query_send_router_reply, query);
288 /* Reply callback called after primary router has replied to our initial
289 sending of the query to it. We will proceed the query in this function. */
291 void silc_server_query_send_router_reply(void *context, void *reply)
293 SilcServerQuery query = context;
294 SilcServer server = query->cmd->server;
296 /* Check if router sent error reply */
297 if (!silc_server_query_check_error(server, query, reply)) {
298 silc_server_query_free(query);
302 /* Continue with parsing */
303 silc_server_query_parse(server, query);
306 /* Parse the command query and start processing the queries in detail. */
308 void silc_server_query_parse(SilcServer server, SilcServerQuery query)
310 SilcServerCommandContext cmd = query->cmd;
312 SilcUInt32 tmp_len, argc = silc_argument_get_arg_num(cmd->args);
317 switch (query->querycmd) {
319 case SILC_COMMAND_WHOIS:
320 /* Get Client IDs if present. Take IDs always instead of nickname. */
321 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
325 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
327 silc_server_query_send_error(server, query,
328 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
329 silc_server_query_free(query);
333 /* Get the nickname@server string and parse it */
334 if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
335 silc_server_query_send_error(server, query,
336 SILC_STATUS_ERR_BAD_NICKNAME, 0);
337 silc_server_query_free(query);
342 /* Parse the IDs included in the query */
343 query->ids = silc_calloc(argc, sizeof(*query->ids));
345 for (i = 0; i < argc; i++) {
346 tmp = silc_argument_get_arg_type(cmd->args, i + 4, &tmp_len);
350 id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
352 silc_server_query_add_error(server, query, TRUE, i + 4,
353 SILC_STATUS_ERR_BAD_CLIENT_ID);
357 query->ids[query->ids_count].id = id;
358 query->ids[query->ids_count].id_type = SILC_ID_CLIENT;
363 /* Get the max count of reply messages allowed */
364 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
365 if (tmp && tmp_len == sizeof(SilcUInt32))
366 SILC_GET32_MSB(query->reply_count, tmp);
368 /* Get requested attributes if set */
369 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
371 query->attrs = silc_attribute_payload_parse_list(tmp, tmp_len);
374 case SILC_COMMAND_WHOWAS:
376 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
378 silc_server_query_send_error(server, query,
379 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
380 silc_server_query_free(query);
384 /* Get the nickname@server string and parse it */
385 if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server)) {
386 silc_server_query_send_error(server, query,
387 SILC_STATUS_ERR_BAD_NICKNAME, 0);
388 silc_server_query_free(query);
392 /* Get the max count of reply messages allowed */
393 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
394 if (tmp && tmp_len == sizeof(SilcUInt32))
395 SILC_GET32_MSB(query->reply_count, tmp);
398 case SILC_COMMAND_IDENTIFY:
399 /* Get IDs if present. Take IDs always instead of names. */
400 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
403 /* Try get nickname */
404 tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
406 /* Get the nickname@server string and parse it */
407 if (!silc_parse_userfqdn(tmp, &query->nickname, &query->nick_server))
408 silc_server_query_add_error(server, query, TRUE, 1,
409 SILC_STATUS_ERR_BAD_NICKNAME);
412 /* Try get server name */
413 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
415 query->server_name = silc_memdup(tmp, tmp_len);
417 /* Get channel name */
418 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
420 query->channel_name = silc_memdup(tmp, tmp_len);
423 /* Parse the IDs included in the query */
424 query->ids = silc_calloc(argc, sizeof(*query->ids));
426 for (i = 0; i < argc; i++) {
427 tmp = silc_argument_get_arg_type(cmd->args, i + 5, &tmp_len);
431 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
433 silc_server_query_add_error(server, query, TRUE, i + 5,
434 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
438 query->ids[query->ids_count].id = id;
439 query->ids[query->ids_count].id_type = id_type;
444 /* Get the max count of reply messages allowed */
445 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
446 if (tmp && tmp_len == sizeof(SilcUInt32))
447 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, client_entry;
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) {
609 silc_server_query_send_reply(server, query, NULL, 0, NULL, 0, NULL, 0);
610 silc_server_query_free(query);
614 /* Now process all found information and if necessary do some more
616 switch (query->querycmd) {
618 case SILC_COMMAND_WHOIS:
619 for (i = 0; i < clients_count; i++) {
620 client_entry = clients[i];
625 /* If requested attributes is set then we always resolve the client
626 information, if not then check whether the entry is complete or not
627 and decide whether we need to resolve or not. */
629 if ((client_entry->nickname && client_entry->username &&
630 client_entry->userinfo) ||
631 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
633 /* Check if cannot query this anyway, so take next one */
634 if (!client_entry->router)
637 /* If we are router, client is local to us, or client is on channel
638 we do not need to resolve the client information. */
639 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
640 || silc_hash_table_count(client_entry->channels))
645 /* When requested attributes is present and local client is detached
646 we cannot send the command to the client, we'll reply on behalf of
647 the client instead. */
648 if (query->attrs && SILC_IS_LOCAL(client_entry) &&
649 client_entry->mode & SILC_UMODE_DETACHED)
652 /* Resolve the detailed client information. If client is local we
653 know that attributes were present and we will resolve directly
654 from the client. Otherwise resolve from client's owner. */
655 silc_server_query_resolve(server, query,
656 (SILC_IS_LOCAL(client_entry) ?
657 client_entry->connection :
658 client_entry->router->connection),
663 case SILC_COMMAND_WHOWAS:
664 for (i = 0; i < clients_count; i++) {
665 client_entry = clients[i];
667 /* Check if cannot query this anyway, so take next one */
668 if (!client_entry || !client_entry->router)
671 /* If both nickname and username are present no resolving is needed */
672 if (client_entry->nickname && client_entry->username)
675 /* Resolve the detailed client information */
676 silc_server_query_resolve(server, query,
677 client_entry->router->connection,
682 case SILC_COMMAND_IDENTIFY:
683 for (i = 0; i < clients_count; i++) {
684 client_entry = clients[i];
686 /* Check if cannot query this anyway, so take next one */
687 if (!client_entry || !client_entry->router)
690 if (client_entry->nickname ||
691 !(client_entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
692 /* If we are router, client is local to us, or client is on channel
693 we do not need to resolve the client information. */
694 if (server->server_type != SILC_SERVER || SILC_IS_LOCAL(client_entry)
695 || silc_hash_table_count(client_entry->channels))
699 /* Resolve the detailed client information */
700 silc_server_query_resolve(server, query,
701 client_entry->router->connection,
707 if (!query->num_query)
708 /* If we didn't have to do any resolving, continue with sending the
709 command reply to the original sender. */
710 silc_server_query_send_reply(server, query, clients, clients_count,
711 servers, servers_count, channels,
714 /* Now actually send the resolvings we gathered earlier */
715 silc_server_query_resolve(server, query, NULL, NULL);
722 /* Resolve the detailed information for the `client_entry'. Only client
723 information needs to be resolved for being incomplete. Each incomplete
724 client entry calls this function to do the resolving. */
726 void silc_server_query_resolve(SilcServer server, SilcServerQuery query,
727 SilcSocketConnection sock,
728 SilcClientEntry client_entry)
733 if (!sock && client_entry)
736 /* If arguments are NULL we will now actually send the resolvings
737 that earlier has been gathered by calling this function. */
738 if (!sock && !client_entry) {
743 if (client_entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
744 /* The entry is being resolved by some other external query already.
745 Attach to that query instead of resolving again. */
746 ident = client_entry->resolve_cmd_ident;
747 silc_server_command_pending(server, SILC_COMMAND_NONE, ident,
748 silc_server_query_resolve_reply, query);
750 ident = ++server->cmd_ident;
752 switch (query->querycmd) {
754 case SILC_COMMAND_WHOIS:
755 case SILC_COMMAND_IDENTIFY:
758 case SILC_COMMAND_WHOWAS:
759 /* We must send WHOWAS command since it's the only the way of
760 resolving clients that are not present in the network anymore. */
761 silc_server_send_command(server, sock, query->querycmd, ident, 1,
762 1, query->nickname, strlen(query->nickname));
766 silc_server_command_pending(server, query->querycmd, ident,
767 silc_server_query_resolve_reply, query);
770 /* Mark the entry as being resolved */
771 client_entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
772 client_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
773 client_entry->resovle_cmd_ident = ident;
775 /* Save the query information, which we will reprocess after we
776 get this and all other queries back. */
777 query->ids = silc_realloc(query->ids, sizeof(*query->ids) *
778 (query->ids_count + 1));
780 query->ids[query->ids_count].id = silc_id_dup(id, id_type);
781 query->ids[query->ids_count].id_type = id_type;
782 query->ids[query->ids_count].ident = ident;
789 /* Reply callback called after one resolving has been completed. If
790 all resolvings has been received then we will continue with sending
791 the command reply to the original sender of the query. */
793 void silc_server_query_resolve_reply(void *context, void *reply)
795 SilcServerQuery query = context;
800 void silc_server_query_send_reply(SilcServer server,
801 SilcServerQuery query,
802 SilcClientEntry *clients,
803 SilcUInt32 clients_count,
804 SilcServerEntry *servers,
805 SilcUInt32 servers_count,
806 SilcChannelEntry *channels,
807 SilcUInt32 channels_count)
812 /* Find client by the Client ID indicated by the `client_id', and if not
813 found then query it by using WHOIS command. The client information
814 is also resolved if the cached information is incomplete or if the
815 `always_resolve' is set to TRUE. The indication whether requested
816 client was being resolved is saved into `resolved'. If the client
817 is not being resolved its entry is returned by this function. NULL
818 is returned if client is resolved. */
820 SilcClientEntry silc_server_query_client(SilcServer server,
821 const SilcClientID *client_id,
825 SilcClientEntry client;
830 client = silc_idlist_find_client_by_id(server->local_list,
831 (SilcClientID *)client_id,
834 client = silc_idlist_find_client_by_id(server->global_list,
835 (SilcClientID *)client_id,
837 if (!client && server->server_type == SILC_ROUTER)
841 if (!client && server->standalone)
844 if (!client || !client->nickname || !client->username ||
846 SilcBuffer buffer, idp;
849 client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
850 client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
851 client->resolve_cmd_ident = ++server->cmd_ident;
854 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
855 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
856 server->cmd_ident, 1,
857 4, idp->data, idp->len);
858 silc_server_packet_send(server, client ? client->router->connection :
859 SILC_PRIMARY_ROUTE(server),
860 SILC_PACKET_COMMAND, 0,
861 buffer->data, buffer->len, FALSE);
862 silc_buffer_free(idp);
863 silc_buffer_free(buffer);