+ SilcGetClientCallback completion;
+ void *context;
+ char *nickname;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+} *GetClientInternal;
+
+/* Completion for IDENTIFY */
+
+SILC_CLIENT_CMD_FUNC(get_client_callback)
+{
+ GetClientInternal i = (GetClientInternal)context;
+ SilcClientEntry *clients;
+ SilcUInt32 clients_count;
+
+ /* Get the clients */
+ clients = silc_client_get_clients_local(i->client, i->conn,
+ i->nickname, NULL,
+ &clients_count);
+ if (clients) {
+ i->completion(i->client, i->conn, clients, clients_count, i->context);
+ silc_free(clients);
+ } else {
+ i->completion(i->client, i->conn, NULL, 0, i->context);
+ }
+
+ silc_free(i->nickname);
+ silc_free(i);
+}
+
+/* Completion for WHOIS */
+
+SILC_CLIENT_CMD_FUNC(get_client_callback_wc)
+{
+ GetClientInternal i = (GetClientInternal)context;
+ SilcClientCommandReplyContext cmd = context2;
+ SilcClientID *client_id = NULL;
+ SilcClientEntry client_entry = NULL;
+ unsigned char *id_data;
+ SilcUInt32 len;
+
+ /* Get the client entry just returned from server */
+ id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
+ if (id_data)
+ client_id = silc_id_payload_parse_id(id_data, len, NULL);
+ if (client_id)
+ client_entry = silc_client_get_client_by_id(i->client,
+ i->conn, client_id);
+ if (!client_entry) {
+ if (!SILC_STATUS_IS_ERROR(cmd->status) &&
+ cmd->status != SILC_STATUS_OK &&
+ cmd->status != SILC_STATUS_LIST_END) {
+ silc_free(client_id);
+ return;
+ }
+
+ i->completion(i->client, i->conn, i->clients, i->clients_count,
+ i->context);
+ silc_free(client_id);
+ silc_free(i->clients);
+ silc_free(i->nickname);
+ silc_free(i);
+ return;
+ }
+
+ /* Save the client */
+ i->clients = silc_realloc(i->clients,
+ (sizeof(*i->clients) * (i->clients_count + 1)));
+ i->clients[i->clients_count] = client_entry;
+ i->clients_count++;
+
+ /* Return if more data is expected */
+ if (cmd->status != SILC_STATUS_OK &&
+ cmd->status != SILC_STATUS_LIST_END) {
+ silc_free(client_id);
+ return;
+ }
+
+ i->completion(i->client, i->conn, i->clients, i->clients_count,
+ i->context);
+
+ silc_free(client_id);
+ silc_free(i->clients);
+ silc_free(i->nickname);
+ silc_free(i);
+}
+
+/* Our own WHOIS reply processor. */
+
+SILC_CLIENT_CMD_FUNC(get_client_callback_w)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+ SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!silc_command_get_status(cmd->payload, NULL, NULL)) {
+ if (SILC_STATUS_IS_ERROR(cmd->status))
+ goto out;
+ if (cmd->status == SILC_STATUS_LIST_END)
+ goto out;
+ goto err;
+ }
+
+ /* Save WHOIS info */
+ silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
+
+ /* Call pending completion for each reply */
+ if (cmd->status != SILC_STATUS_OK &&
+ cmd->status != SILC_STATUS_LIST_END) {
+ if (cmd->callbacks[0].callback)
+ (*cmd->callbacks[0].callback)(cmd->callbacks[0].context, cmd);
+ silc_client_command_reply_free(cmd);
+ return;
+ }
+
+ out:
+ SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+
+ err:
+ /* If we received notify for invalid ID we'll remove the ID if we
+ have it cached. */
+ if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+ SilcClientEntry client_entry;
+ SilcUInt32 tmp_len;
+ unsigned char *tmp =
+ silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+ 2, &tmp_len);
+ if (tmp) {
+ SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (client_id) {
+ client_entry = silc_client_get_client_by_id(cmd->client, conn,
+ client_id);
+ if (client_entry)
+ silc_client_del_client(cmd->client, conn, client_entry);
+ silc_free(client_id);
+ }
+ }
+ }
+
+ /* Unregister this command reply */
+ silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
+ NULL, silc_client_command_reply_whois_i,
+ cmd->ident);
+ silc_client_command_reply_free(cmd);
+}
+
+/* Finds client entry or entries by the `nickname' and `server'. The
+ completion callback will be called when the client entries has been found.
+
+ Note: this function is always asynchronous and resolves the client
+ information from the server. Thus, if you already know the client
+ information then use the silc_client_get_client_by_id function to
+ get the client entry since this function may be very slow and should
+ be used only to initially get the client entries. */
+
+void silc_client_get_clients_i(SilcClient client,
+ SilcClientConnection conn,
+ SilcCommand command,
+ const char *nickname,
+ const char *server,
+ SilcBuffer attributes,
+ SilcGetClientCallback completion,
+ void *context)
+{
+ GetClientInternal i;
+ int len;
+ char *userhost = NULL;
+
+ assert(client && conn);
+
+ if (!nickname && !attributes)
+ return;
+
+ i = silc_calloc(1, sizeof(*i));
+ i->client = client;
+ i->conn = conn;
+ i->nickname = nickname ? strdup(nickname) : NULL;
+ i->completion = completion;
+ i->context = context;
+
+ if (nickname && server) {
+ len = strlen(nickname) + strlen(server) + 3;
+ userhost = silc_calloc(len, sizeof(*userhost));
+ silc_strncat(userhost, len, nickname, strlen(nickname));
+ silc_strncat(userhost, len, "@", 1);
+ silc_strncat(userhost, len, server, strlen(server));
+ } else if (nickname) {
+ userhost = silc_memdup(nickname, strlen(nickname));
+ }
+
+ /* Register our own command reply for this command */
+ if (command == SILC_COMMAND_IDENTIFY) {
+ silc_client_command_register(client, command, NULL, NULL,
+ silc_client_command_reply_identify_i, 0,
+ ++conn->cmd_ident);
+ /* Send the command */
+ silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+ conn->cmd_ident, 1, 1, userhost,
+ strlen(userhost));
+
+ /* Add pending callback */
+ silc_client_command_pending(conn, command, conn->cmd_ident,
+ silc_client_command_get_client_callback,
+ (void *)i);
+ } else {
+ silc_client_command_register(client, command, NULL, NULL,
+ silc_client_command_get_client_callback_w, 0,
+ ++conn->cmd_ident);
+ /* Send the command */
+ silc_client_command_send(client, conn, command, conn->cmd_ident, 2,
+ 1, userhost, userhost ? strlen(userhost) : 0,
+ 3, attributes ? attributes->data : NULL,
+ attributes ? attributes->len : 0);
+
+ /* Add pending callback */
+ silc_client_command_pending(conn, command, conn->cmd_ident,
+ silc_client_command_get_client_callback_wc,
+ (void *)i);
+ }
+ silc_free(userhost);
+}
+
+void silc_client_get_clients(SilcClient client,
+ SilcClientConnection conn,
+ const char *nickname,
+ const char *server,
+ SilcGetClientCallback completion,
+ void *context)
+{
+ silc_client_get_clients_i(client, conn, SILC_COMMAND_IDENTIFY,
+ nickname, server, NULL,
+ completion, context);
+}
+
+void silc_client_get_clients_whois(SilcClient client,
+ SilcClientConnection conn,
+ const char *nickname,
+ const char *server,
+ SilcBuffer attributes,
+ SilcGetClientCallback completion,
+ void *context)
+{
+ silc_client_get_clients_i(client, conn, SILC_COMMAND_WHOIS,
+ nickname, server, attributes,
+ completion, context);
+}
+
+/* The old style function to find client entry. This is used by the
+ library internally. If `query' is TRUE then the client information is
+ requested by the server. The pending command callback must be set
+ by the caller. */
+/* XXX This function should be removed */
+
+SilcClientEntry silc_idlist_get_client(SilcClient client,
+ SilcClientConnection conn,
+ const char *nickname,
+ const char *format,
+ bool query)
+{
+ SilcIDCacheEntry id_cache;
+ SilcIDCacheList list = NULL;
+ SilcClientEntry entry = NULL;
+ char *nicknamec;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Normalize nickname for search */
+ nicknamec = silc_identifier_check(nickname, strlen(nickname),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!nicknamec)
+ return NULL;
+
+ /* Find ID from cache */
+ if (!silc_idcache_find_by_name(conn->internal->client_cache,
+ nicknamec, &list)) {
+ identify:
+
+ if (query) {
+ SILC_LOG_DEBUG(("Requesting Client ID from server"));
+
+ /* Register our own command reply for this command */
+ silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+ silc_client_command_reply_identify_i, 0,
+ ++conn->cmd_ident);
+
+ /* Send the command */
+ silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+ conn->cmd_ident, 1, 1, nickname,
+ strlen(nickname));
+
+ if (list)
+ silc_idcache_list_free(list);
+
+ silc_free(nicknamec);
+ return NULL;
+ }
+
+ silc_free(nicknamec);
+ return NULL;
+ }
+
+ if (!format) {
+ /* Take first found cache entry */
+ if (!silc_idcache_list_first(list, &id_cache))
+ goto identify;
+
+ entry = (SilcClientEntry)id_cache->context;
+ } else {
+ /* Check multiple cache entries for match */
+ silc_idcache_list_first(list, &id_cache);
+ while (id_cache) {
+ entry = (SilcClientEntry)id_cache->context;
+
+ if (!silc_utf8_strcasecmp(entry->nickname, format)) {
+ if (!silc_idcache_list_next(list, &id_cache)) {
+ entry = NULL;
+ break;
+ } else {
+ entry = NULL;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ /* If match weren't found, request it */
+ if (!entry)
+ goto identify;
+ }
+
+ silc_free(nicknamec);
+
+ if (list)
+ silc_idcache_list_free(list);
+
+ return entry;
+}
+
+typedef struct {
+ SilcClient client;
+ SilcClientConnection conn;
+ SilcUInt32 list_count;