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;
129 /* Find ID from cache */
130 if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
133 if (!silc_idcache_list_count(list)) {
134 silc_idcache_list_free(list);
138 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
139 *clients_count = silc_idcache_list_count(list);
142 /* Take all without any further checking */
143 silc_idcache_list_first(list, &id_cache);
145 clients[i++] = id_cache->context;
147 if (!silc_idcache_list_next(list, &id_cache))
151 /* Check multiple cache entries for match */
152 silc_idcache_list_first(list, &id_cache);
154 entry = (SilcClientEntry)id_cache->context;
157 strncasecmp(server, entry->server, strlen(server))) {
158 if (!silc_idcache_list_next(list, &id_cache)) {
165 clients[i++] = id_cache->context;
167 if (!silc_idcache_list_next(list, &id_cache))
173 silc_idcache_list_free(list);
187 SilcClientConnection conn;
189 SilcBuffer client_id_list;
190 SilcGetClientCallback completion;
193 } *GetClientsByListInternal;
195 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
197 GetClientsByListInternal i = (GetClientsByListInternal)context;
198 SilcIDCacheEntry id_cache = NULL;
199 SilcBuffer client_id_list = i->client_id_list;
200 SilcClientEntry *clients = NULL;
201 uint32 clients_count = 0;
204 for (c = 0; c < i->list_count; c++) {
206 SilcClientID *client_id;
209 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
211 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
215 /* Get the client entry */
216 if (silc_idcache_find_by_id_one_ext(i->conn->client_cache,
219 silc_hash_client_id_compare, NULL,
221 clients = silc_realloc(clients, sizeof(*clients) *
222 (clients_count + 1));
223 clients[clients_count] = (SilcClientEntry)id_cache->context;
228 silc_free(client_id);
229 silc_buffer_pull(client_id_list, idp_len);
233 i->completion(i->client, i->conn, clients, clients_count, i->context);
238 static void silc_client_get_clients_list_destructor(void *context)
240 GetClientsByListInternal i = (GetClientsByListInternal)context;
242 if (i->found == FALSE)
243 i->completion(i->client, i->conn, NULL, 0, i->context);
245 if (i->client_id_list)
246 silc_buffer_free(i->client_id_list);
250 /* Gets client entries by the list of client ID's `client_id_list'. This
251 always resolves those client ID's it does not know yet from the server
252 so this function might take a while. The `client_id_list' is a list
253 of ID Payloads added one after other. JOIN command reply and USERS
254 command reply for example returns this sort of list. The `completion'
255 will be called after the entries are available. */
257 void silc_client_get_clients_by_list(SilcClient client,
258 SilcClientConnection conn,
260 SilcBuffer client_id_list,
261 SilcGetClientCallback completion,
264 SilcIDCacheEntry id_cache = NULL;
266 unsigned char **res_argv = NULL;
267 uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
268 GetClientsByListInternal in;
270 in = silc_calloc(1, sizeof(*in));
273 in->list_count = list_count;
274 in->client_id_list = silc_buffer_copy(client_id_list);
275 in->completion = completion;
276 in->context = context;
278 for (i = 0; i < list_count; i++) {
280 SilcClientID *client_id;
281 SilcClientEntry entry;
284 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
286 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
290 /* Check if we have this client cached already. */
292 silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
294 silc_hash_client_id_compare, NULL,
297 /* If we don't have the entry or it has incomplete info, then resolve
298 it from the server. */
299 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
300 if (!id_cache || !entry->nickname) {
301 /* No we don't have it, query it from the server. Assemble argument
302 table that will be sent fr the IDENTIFY command later. */
303 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
305 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
307 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
309 res_argv[res_argc] = client_id_list->data;
310 res_argv_lens[res_argc] = idp_len;
311 res_argv_types[res_argc] = res_argc + 5;
315 silc_free(client_id);
316 silc_buffer_pull(client_id_list, idp_len);
319 /* Query the client information from server if the list included clients
320 that we don't know about. */
324 /* Send the IDENTIFY command to server */
325 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
326 res_argc, res_argv, res_argv_lens,
327 res_argv_types, ++conn->cmd_ident);
328 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
329 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
332 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
334 silc_client_get_clients_list_destructor,
335 silc_client_command_get_clients_list_callback,
338 silc_buffer_push(client_id_list, client_id_list->data -
339 client_id_list->head);
340 silc_buffer_free(res_cmd);
342 silc_free(res_argv_lens);
343 silc_free(res_argv_types);
347 silc_buffer_push(client_id_list, client_id_list->data -
348 client_id_list->head);
350 /* We have the clients in cache, get them and call the completion */
351 silc_client_command_get_clients_list_callback((void *)in, NULL);
354 /* The old style function to find client entry. This is used by the
355 library internally. If `query' is TRUE then the client information is
356 requested by the server. The pending command callback must be set
359 SilcClientEntry silc_idlist_get_client(SilcClient client,
360 SilcClientConnection conn,
366 SilcIDCacheEntry id_cache;
367 SilcIDCacheList list = NULL;
368 SilcClientEntry entry = NULL;
370 /* Find ID from cache */
371 if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
376 SilcClientCommandContext ctx;
378 SILC_LOG_DEBUG(("Requesting Client ID from server"));
380 /* No ID found. Do query from the server. The query is done by
381 sending simple IDENTIFY command to the server. */
382 ctx = silc_client_command_alloc();
383 ctx->client = client;
385 ctx->command = silc_client_command_find("IDENTIFY");
386 memset(ident, 0, sizeof(ident));
387 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
388 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
389 &ctx->argv_types, &ctx->argc, 2);
390 ctx->command->cb(ctx, NULL);
393 silc_idcache_list_free(list);
400 if (!server && !num) {
401 /* Take first found cache entry */
402 if (!silc_idcache_list_first(list, &id_cache))
405 entry = (SilcClientEntry)id_cache->context;
407 /* Check multiple cache entries for match */
408 silc_idcache_list_first(list, &id_cache);
409 entry = (SilcClientEntry)id_cache->context;
412 if (server && entry->server &&
413 !strncasecmp(server, entry->server, strlen(server)))
416 if (num && entry->num == num)
419 if (!silc_idcache_list_next(list, &id_cache)) {
424 entry = (SilcClientEntry)id_cache->context;
427 /* If match weren't found, request it */
433 silc_idcache_list_free(list);
438 /* Finds entry for client by the client's ID. Returns the entry or NULL
439 if the entry was not found. */
441 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
442 SilcClientConnection conn,
443 SilcClientID *client_id)
445 SilcIDCacheEntry id_cache;
447 SILC_LOG_DEBUG(("Finding client by ID (%s)",
448 silc_id_render(client_id, SILC_ID_CLIENT)));
450 /* Find ID from cache */
451 if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
453 silc_hash_client_id_compare, NULL,
457 SILC_LOG_DEBUG(("Found"));
459 return (SilcClientEntry)id_cache->context;
464 SilcClientConnection conn;
465 SilcClientID *client_id;
466 SilcGetClientCallback completion;
469 } *GetClientByIDInternal;
471 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
473 GetClientByIDInternal i = (GetClientByIDInternal)context;
474 SilcClientEntry entry;
477 entry = silc_client_get_client_by_id(i->client, i->conn,
480 i->completion(i->client, i->conn, &entry, 1, i->context);
485 static void silc_client_get_client_by_id_destructor(void *context)
487 GetClientByIDInternal i = (GetClientByIDInternal)context;
489 if (i->found == FALSE)
490 i->completion(i->client, i->conn, NULL, 0, i->context);
493 silc_free(i->client_id);
497 /* Same as above but will always resolve the information from the server.
498 Use this only if you know that you don't have the entry and the only
499 thing you know about the client is its ID. */
501 void silc_client_get_client_by_id_resolve(SilcClient client,
502 SilcClientConnection conn,
503 SilcClientID *client_id,
504 SilcGetClientCallback completion,
508 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
510 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
511 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
513 1, 3, idp->data, idp->len);
514 silc_buffer_free(idp);
518 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
519 i->completion = completion;
520 i->context = context;
522 /* Add pending callback */
523 silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
525 silc_client_get_client_by_id_destructor,
526 silc_client_command_get_client_by_id_callback,
530 /* Deletes the client entry and frees all memory. */
532 void silc_client_del_client_entry(SilcClient client,
533 SilcClientEntry client_entry)
535 silc_free(client_entry->nickname);
536 silc_free(client_entry->username);
537 silc_free(client_entry->realname);
538 silc_free(client_entry->server);
539 silc_free(client_entry->id);
540 if (client_entry->send_key)
541 silc_cipher_free(client_entry->send_key);
542 if (client_entry->receive_key)
543 silc_cipher_free(client_entry->receive_key);
544 silc_free(client_entry->key);
545 silc_free(client_entry);
548 /* Removes client from the cache by the client entry. */
550 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
551 SilcClientEntry client_entry)
553 bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
554 silc_client_del_client_entry(client, client_entry);
558 /* Removes channel from the cache by the channel entry. */
560 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
561 SilcChannelEntry channel)
563 bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
564 silc_free(channel->channel_name);
565 silc_free(channel->id);
566 silc_free(channel->key);
567 if (channel->channel_key)
568 silc_cipher_free(channel->channel_key);
570 silc_hmac_free(channel->hmac);
571 silc_client_del_channel_private_keys(client, conn, channel);
576 /* Finds entry for channel by the channel name. Returns the entry or NULL
577 if the entry was not found. It is found only if the client is joined
580 SilcChannelEntry silc_client_get_channel(SilcClient client,
581 SilcClientConnection conn,
584 SilcIDCacheEntry id_cache;
585 SilcChannelEntry entry;
587 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
591 entry = (SilcChannelEntry)id_cache->context;
596 /* Finds entry for channel by the channel ID. Returns the entry or NULL
597 if the entry was not found. It is found only if the client is joined
600 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
601 SilcClientConnection conn,
602 SilcChannelID *channel_id)
604 SilcIDCacheEntry id_cache;
605 SilcChannelEntry entry;
607 if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id,
611 entry = (SilcChannelEntry)id_cache->context;
618 SilcClientConnection conn;
619 SilcChannelID *channel_id;
620 SilcGetChannelCallback completion;
623 } *GetChannelByIDInternal;
625 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
627 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
628 SilcChannelEntry entry;
630 /* Get the channel */
631 entry = silc_client_get_channel_by_id(i->client, i->conn,
634 i->completion(i->client, i->conn, &entry, 1, i->context);
639 static void silc_client_get_channel_by_id_destructor(void *context)
641 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
643 if (i->found == FALSE)
644 i->completion(i->client, i->conn, NULL, 0, i->context);
646 silc_free(i->channel_id);
650 /* Resolves channel information from the server by the channel ID. */
652 void silc_client_get_channel_by_id_resolve(SilcClient client,
653 SilcClientConnection conn,
654 SilcChannelID *channel_id,
655 SilcGetChannelCallback completion,
659 GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
661 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
662 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
664 1, 5, idp->data, idp->len);
665 silc_buffer_free(idp);
669 i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
670 i->completion = completion;
671 i->context = context;
673 /* Add pending callback */
674 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
676 silc_client_get_channel_by_id_destructor,
677 silc_client_command_get_channel_by_id_callback,
681 /* Find channel entry by ID. This routine is used internally by the library. */
683 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
684 SilcClientConnection conn,
685 SilcChannelID *channel_id,
689 SilcChannelEntry channel;
691 channel = silc_client_get_channel_by_id(client, conn, channel_id);
696 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
697 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
699 1, 5, idp->data, idp->len);
700 silc_buffer_free(idp);
706 /* Finds entry for server by the server name. */
708 SilcServerEntry silc_client_get_server(SilcClient client,
709 SilcClientConnection conn,
712 SilcIDCacheEntry id_cache;
713 SilcServerEntry entry;
715 if (!silc_idcache_find_by_name_one(conn->server_cache, server_name,
719 entry = (SilcServerEntry)id_cache->context;
724 /* Finds entry for server by the server ID. */
726 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
727 SilcClientConnection conn,
728 SilcServerID *server_id)
730 SilcIDCacheEntry id_cache;
731 SilcServerEntry entry;
733 if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
737 entry = (SilcServerEntry)id_cache->context;
742 /* Removes server from the cache by the server entry. */
744 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
745 SilcServerEntry server)
747 bool ret = silc_idcache_del_by_context(conn->server_cache, server);
748 silc_free(server->server_name);
749 silc_free(server->server_info);
750 silc_free(server->server_id);