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);
97 i->cmd = silc_client_command_dup(ctx);
98 i->nickname = nickname ? strdup(nickname) : NULL;
99 i->server = server ? strdup(server) : NULL;
100 i->completion = completion;
101 i->context = context;
103 /* Call the command */
104 ctx->command->cb(ctx, NULL);
106 /* Add pending callback */
107 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
109 silc_client_get_client_destructor,
110 silc_client_command_get_client_callback,
114 /* Same as above function but does not resolve anything from the server.
115 This checks local cache and returns all clients from the cache. */
117 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
118 SilcClientConnection conn,
121 uint32 *clients_count)
123 SilcIDCacheEntry id_cache;
124 SilcIDCacheList list = NULL;
125 SilcClientEntry entry, *clients;
128 /* Find ID from cache */
129 if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
132 if (!silc_idcache_list_count(list)) {
133 silc_idcache_list_free(list);
137 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
138 *clients_count = silc_idcache_list_count(list);
141 /* Take all without any further checking */
142 silc_idcache_list_first(list, &id_cache);
144 clients[i++] = id_cache->context;
145 if (!silc_idcache_list_next(list, &id_cache))
149 /* Check multiple cache entries for match */
150 silc_idcache_list_first(list, &id_cache);
152 entry = (SilcClientEntry)id_cache->context;
155 strncasecmp(server, entry->server, strlen(server))) {
156 if (!silc_idcache_list_next(list, &id_cache)) {
163 clients[i++] = id_cache->context;
164 if (!silc_idcache_list_next(list, &id_cache))
170 silc_idcache_list_free(list);
177 SilcClientConnection conn;
179 SilcBuffer client_id_list;
180 SilcGetClientCallback completion;
183 } *GetClientsByListInternal;
185 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
187 GetClientsByListInternal i = (GetClientsByListInternal)context;
188 SilcIDCacheEntry id_cache = NULL;
189 SilcBuffer client_id_list = i->client_id_list;
190 SilcClientEntry *clients = NULL;
191 uint32 clients_count = 0;
194 for (c = 0; c < i->list_count; c++) {
196 SilcClientID *client_id;
199 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
201 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
205 /* Get the client entry */
206 if (silc_idcache_find_by_id_one_ext(i->conn->client_cache,
209 silc_hash_client_id_compare, NULL,
211 clients = silc_realloc(clients, sizeof(*clients) *
212 (clients_count + 1));
213 clients[clients_count] = (SilcClientEntry)id_cache->context;
218 silc_free(client_id);
219 silc_buffer_pull(client_id_list, idp_len);
223 i->completion(i->client, i->conn, clients, clients_count, i->context);
228 static void silc_client_get_clients_list_destructor(void *context)
230 GetClientsByListInternal i = (GetClientsByListInternal)context;
232 if (i->found == FALSE)
233 i->completion(i->client, i->conn, NULL, 0, i->context);
235 if (i->client_id_list)
236 silc_buffer_free(i->client_id_list);
240 /* Gets client entries by the list of client ID's `client_id_list'. This
241 always resolves those client ID's it does not know yet from the server
242 so this function might take a while. The `client_id_list' is a list
243 of ID Payloads added one after other. JOIN command reply and USERS
244 command reply for example returns this sort of list. The `completion'
245 will be called after the entries are available. */
247 void silc_client_get_clients_by_list(SilcClient client,
248 SilcClientConnection conn,
250 SilcBuffer client_id_list,
251 SilcGetClientCallback completion,
254 SilcIDCacheEntry id_cache = NULL;
256 unsigned char **res_argv = NULL;
257 uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
258 GetClientsByListInternal in;
260 in = silc_calloc(1, sizeof(*in));
263 in->list_count = list_count;
264 in->client_id_list = silc_buffer_copy(client_id_list);
265 in->completion = completion;
266 in->context = context;
268 for (i = 0; i < list_count; i++) {
270 SilcClientID *client_id;
271 SilcClientEntry entry;
274 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
276 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
280 /* Check if we have this client cached already. */
282 silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
284 silc_hash_client_id_compare, NULL,
287 /* If we don't have the entry or it has incomplete info, then resolve
288 it from the server. */
289 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
290 if (!id_cache || !entry->nickname) {
291 /* No we don't have it, query it from the server. Assemble argument
292 table that will be sent fr the IDENTIFY command later. */
293 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
295 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
297 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
299 res_argv[res_argc] = client_id_list->data;
300 res_argv_lens[res_argc] = idp_len;
301 res_argv_types[res_argc] = res_argc + 3;
305 silc_free(client_id);
306 silc_buffer_pull(client_id_list, idp_len);
309 /* Query the client information from server if the list included clients
310 that we don't know about. */
314 /* Send the IDENTIFY command to server */
315 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
316 res_argc, res_argv, res_argv_lens,
317 res_argv_types, ++conn->cmd_ident);
318 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
319 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
322 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
324 silc_client_get_clients_list_destructor,
325 silc_client_command_get_clients_list_callback,
328 silc_buffer_push(client_id_list, client_id_list->data -
329 client_id_list->head);
330 silc_buffer_free(res_cmd);
332 silc_free(res_argv_lens);
333 silc_free(res_argv_types);
337 silc_buffer_push(client_id_list, client_id_list->data -
338 client_id_list->head);
340 /* We have the clients in cache, get them and call the completion */
341 silc_client_command_get_clients_list_callback((void *)in, NULL);
344 /* The old style function to find client entry. This is used by the
345 library internally. If `query' is TRUE then the client information is
346 requested by the server. The pending command callback must be set
349 SilcClientEntry silc_idlist_get_client(SilcClient client,
350 SilcClientConnection conn,
356 SilcIDCacheEntry id_cache;
357 SilcIDCacheList list = NULL;
358 SilcClientEntry entry = NULL;
360 /* Find ID from cache */
361 if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
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, NULL);
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 /* Removes client from the cache by the client entry. */
522 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
523 SilcClientEntry client_entry)
525 return silc_idcache_del_by_context(conn->client_cache, client_entry);
528 /* Removes client from the cache by the client ID. */
530 bool silc_client_del_client_by_id(SilcClient client,
531 SilcClientConnection conn,
532 SilcClientID *client_id)
534 return silc_idcache_del_by_id_ext(conn->client_cache, (void *)client_id,
536 silc_hash_client_id_compare, NULL);
539 /* Finds entry for channel by the channel name. Returns the entry or NULL
540 if the entry was not found. It is found only if the client is joined
543 SilcChannelEntry silc_client_get_channel(SilcClient client,
544 SilcClientConnection conn,
547 SilcIDCacheEntry id_cache;
548 SilcChannelEntry entry;
550 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
554 entry = (SilcChannelEntry)id_cache->context;
559 /* Finds entry for server by the server ID. */
561 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
562 SilcClientConnection conn,
563 SilcServerID *server_id)
565 SilcIDCacheEntry id_cache;
566 SilcServerEntry entry;
568 if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
572 entry = (SilcServerEntry)id_cache->context;