5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "clientlibincludes.h"
25 SilcClientCommandContext cmd;
26 SilcGetClientCallback completion;
33 SILC_CLIENT_CMD_FUNC(get_client_callback)
35 GetClientInternal i = (GetClientInternal)context;
36 SilcClientEntry *clients;
40 clients = silc_client_get_clients_local(i->cmd->client, i->cmd->conn,
41 i->nickname, i->server,
44 i->completion(i->cmd->client, i->cmd->conn, clients,
45 clients_count, i->context);
51 static void silc_client_get_client_destructor(void *context)
53 GetClientInternal i = (GetClientInternal)context;
55 if (i->found == FALSE)
56 i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context);
58 silc_client_command_free(i->cmd);
60 silc_free(i->nickname);
66 /* Finds client entry or entries by the `nickname' and `server'. The
67 completion callback will be called when the client entries has been found.
69 Note: this function is always asynchronous and resolves the client
70 information from the server. Thus, if you already know the client
71 information then use the silc_client_get_client_by_id function to
72 get the client entry since this function may be very slow and should
73 be used only to initially get the client entries. */
75 void silc_client_get_clients(SilcClient client,
76 SilcClientConnection conn,
79 SilcGetClientCallback completion,
83 SilcClientCommandContext ctx;
84 GetClientInternal i = silc_calloc(1, sizeof(*i));
86 /* No ID found. Do query from the server. The query is done by
87 sending simple IDENTIFY command to the server. */
88 ctx = silc_client_command_alloc();
91 ctx->command = silc_client_command_find("IDENTIFY");
92 memset(ident, 0, sizeof(ident));
93 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
94 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
95 &ctx->argv_types, &ctx->argc, 2);
96 ctx->command->cb(ctx);
99 i->nickname = nickname ? strdup(nickname) : NULL;
100 i->server = server ? strdup(server) : NULL;
101 i->completion = completion;
102 i->context = context;
104 /* Add pending callback */
105 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
107 silc_client_get_client_destructor,
108 silc_client_command_get_client_callback,
112 /* Same as above function but does not resolve anything from the server.
113 This checks local cache and returns all clients from the cache. */
115 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
116 SilcClientConnection conn,
119 uint32 *clients_count)
121 SilcIDCacheEntry id_cache;
122 SilcIDCacheList list = NULL;
123 SilcClientEntry entry, *clients;
126 /* Find ID from cache */
127 if (!silc_idcache_find_by_name(conn->client_cache, nickname,
131 if (!silc_idcache_list_count(list)) {
132 silc_idcache_list_free(list);
136 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
137 *clients_count = silc_idcache_list_count(list);
140 /* Take all without any further checking */
141 silc_idcache_list_first(list, &id_cache);
143 clients[i++] = id_cache->context;
144 if (!silc_idcache_list_next(list, &id_cache))
148 /* Check multiple cache entries for match */
149 silc_idcache_list_first(list, &id_cache);
151 entry = (SilcClientEntry)id_cache->context;
154 strncasecmp(server, entry->server, strlen(server))) {
155 if (!silc_idcache_list_next(list, &id_cache)) {
162 clients[i++] = id_cache->context;
163 if (!silc_idcache_list_next(list, &id_cache))
169 silc_idcache_list_free(list);
176 SilcClientConnection conn;
178 SilcBuffer client_id_list;
179 SilcGetClientCallback completion;
182 } *GetClientsByListInternal;
184 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
186 GetClientsByListInternal i = (GetClientsByListInternal)context;
187 SilcIDCacheEntry id_cache = NULL;
188 SilcBuffer client_id_list = i->client_id_list;
189 SilcClientEntry *clients = NULL;
190 uint32 clients_count = 0;
193 for (c = 0; c < i->list_count; c++) {
195 SilcClientID *client_id;
198 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
200 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
204 /* Get the client entry */
205 if (silc_idcache_find_by_id_one_ext(i->conn->client_cache,
208 silc_hash_client_id_compare, NULL,
210 clients = silc_realloc(clients, sizeof(*clients) *
211 (clients_count + 1));
212 clients[clients_count] = (SilcClientEntry)id_cache->context;
217 silc_free(client_id);
218 silc_buffer_pull(client_id_list, idp_len);
222 i->completion(i->client, i->conn, clients, clients_count, i->context);
227 static void silc_client_get_clients_list_destructor(void *context)
229 GetClientsByListInternal i = (GetClientsByListInternal)context;
231 if (i->found == FALSE)
232 i->completion(i->client, i->conn, NULL, 0, i->context);
234 if (i->client_id_list)
235 silc_buffer_free(i->client_id_list);
239 /* Gets client entries by the list of client ID's `client_id_list'. This
240 always resolves those client ID's it does not know yet from the server
241 so this function might take a while. The `client_id_list' is a list
242 of ID Payloads added one after other. JOIN command reply and USERS
243 command reply for example returns this sort of list. The `completion'
244 will be called after the entries are available. */
246 void silc_client_get_clients_by_list(SilcClient client,
247 SilcClientConnection conn,
249 SilcBuffer client_id_list,
250 SilcGetClientCallback completion,
253 SilcIDCacheEntry id_cache = NULL;
255 unsigned char **res_argv = NULL;
256 uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
257 GetClientsByListInternal in;
259 in = silc_calloc(1, sizeof(*in));
262 in->list_count = list_count;
263 in->client_id_list = silc_buffer_copy(client_id_list);
264 in->completion = completion;
265 in->context = context;
267 for (i = 0; i < list_count; i++) {
269 SilcClientID *client_id;
270 SilcClientEntry entry;
273 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
275 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
279 /* Check if we have this client cached already. */
281 silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
283 silc_hash_client_id_compare, NULL,
286 /* If we don't have the entry or it has incomplete info, then resolve
287 it from the server. */
288 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
289 if (!id_cache || !entry->nickname) {
290 /* No we don't have it, query it from the server. Assemble argument
291 table that will be sent fr the IDENTIFY command later. */
292 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
294 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
296 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
298 res_argv[res_argc] = client_id_list->data;
299 res_argv_lens[res_argc] = idp_len;
300 res_argv_types[res_argc] = res_argc + 3;
304 silc_free(client_id);
305 silc_buffer_pull(client_id_list, idp_len);
308 /* Query the client information from server if the list included clients
309 that we don't know about. */
313 /* Send the IDENTIFY command to server */
314 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
315 res_argc, res_argv, res_argv_lens,
316 res_argv_types, ++conn->cmd_ident);
317 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
318 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
321 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
323 silc_client_get_clients_list_destructor,
324 silc_client_command_get_clients_list_callback,
327 silc_buffer_push(client_id_list, client_id_list->data -
328 client_id_list->head);
329 silc_buffer_free(res_cmd);
331 silc_free(res_argv_lens);
332 silc_free(res_argv_types);
336 silc_buffer_push(client_id_list, client_id_list->data -
337 client_id_list->head);
339 /* We have the clients in cache, get them and call the completion */
340 silc_client_command_get_clients_list_callback((void *)in);
343 /* The old style function to find client entry. This is used by the
344 library internally. If `query' is TRUE then the client information is
345 requested by the server. The pending command callback must be set
348 SilcClientEntry silc_idlist_get_client(SilcClient client,
349 SilcClientConnection conn,
355 SilcIDCacheEntry id_cache;
356 SilcIDCacheList list = NULL;
357 SilcClientEntry entry = NULL;
359 /* Find ID from cache */
360 if (!silc_idcache_find_by_name(conn->client_cache, nickname,
366 SilcClientCommandContext ctx;
368 SILC_LOG_DEBUG(("Requesting Client ID from server"));
370 /* No ID found. Do query from the server. The query is done by
371 sending simple IDENTIFY command to the server. */
372 ctx = silc_client_command_alloc();
373 ctx->client = client;
375 ctx->command = silc_client_command_find("IDENTIFY");
376 memset(ident, 0, sizeof(ident));
377 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
378 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
379 &ctx->argv_types, &ctx->argc, 2);
380 ctx->command->cb(ctx);
383 silc_idcache_list_free(list);
390 if (!server && !num) {
391 /* Take first found cache entry */
392 if (!silc_idcache_list_first(list, &id_cache))
395 entry = (SilcClientEntry)id_cache->context;
397 /* Check multiple cache entries for match */
398 silc_idcache_list_first(list, &id_cache);
399 entry = (SilcClientEntry)id_cache->context;
402 if (server && entry->server &&
403 !strncasecmp(server, entry->server, strlen(server)))
406 if (num && entry->num == num)
409 if (!silc_idcache_list_next(list, &id_cache)) {
414 entry = (SilcClientEntry)id_cache->context;
417 /* If match weren't found, request it */
423 silc_idcache_list_free(list);
428 /* Finds entry for client by the client's ID. Returns the entry or NULL
429 if the entry was not found. */
431 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
432 SilcClientConnection conn,
433 SilcClientID *client_id)
435 SilcIDCacheEntry id_cache;
437 SILC_LOG_DEBUG(("Finding client by ID (%s)",
438 silc_id_render(client_id, SILC_ID_CLIENT)));
440 /* Find ID from cache */
441 if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
443 silc_hash_client_id_compare, NULL,
447 SILC_LOG_DEBUG(("Found"));
449 return (SilcClientEntry)id_cache->context;
454 SilcClientConnection conn;
455 SilcClientID *client_id;
456 SilcGetClientCallback completion;
459 } *GetClientByIDInternal;
461 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
463 GetClientByIDInternal i = (GetClientByIDInternal)context;
464 SilcClientEntry entry;
467 entry = silc_client_get_client_by_id(i->client, i->conn,
470 i->completion(i->client, i->conn, &entry, 1, i->context);
475 static void silc_client_get_client_by_id_destructor(void *context)
477 GetClientByIDInternal i = (GetClientByIDInternal)context;
479 if (i->found == FALSE)
480 i->completion(i->client, i->conn, NULL, 0, i->context);
483 silc_free(i->client_id);
487 /* Same as above but will always resolve the information from the server.
488 Use this only if you know that you don't have the entry and the only
489 thing you know about the client is its ID. */
491 void silc_client_get_client_by_id_resolve(SilcClient client,
492 SilcClientConnection conn,
493 SilcClientID *client_id,
494 SilcGetClientCallback completion,
498 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
500 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
501 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
503 1, 3, idp->data, idp->len);
504 silc_buffer_free(idp);
508 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
509 i->completion = completion;
510 i->context = context;
512 /* Add pending callback */
513 silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
515 silc_client_get_client_by_id_destructor,
516 silc_client_command_get_client_by_id_callback,
520 /* Finds entry for channel by the channel name. Returns the entry or NULL
521 if the entry was not found. It is found only if the client is joined
524 SilcChannelEntry silc_client_get_channel(SilcClient client,
525 SilcClientConnection conn,
528 SilcIDCacheEntry id_cache;
529 SilcChannelEntry entry;
531 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
535 entry = (SilcChannelEntry)id_cache->context;