5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 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 silc_client_get_clients function but does not resolve anything
115 from the server. This checks local cache and returns all matching
116 clients from the local cache. If none was found this returns NULL.
117 The `nickname' is the real nickname of the client, and the `format'
118 is the formatted nickname to find exact match from multiple found
119 entries. The format must be same as given in the SilcClientParams
120 structure to the client library. If the `format' is NULL all found
121 clients by `nickname' are returned. */
123 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
124 SilcClientConnection conn,
125 const char *nickname,
127 uint32 *clients_count)
129 SilcIDCacheEntry id_cache;
130 SilcIDCacheList list = NULL;
131 SilcClientEntry entry, *clients;
135 /* Find ID from cache */
136 if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
139 if (!silc_idcache_list_count(list)) {
140 silc_idcache_list_free(list);
144 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
145 *clients_count = silc_idcache_list_count(list);
148 /* Take all without any further checking */
149 silc_idcache_list_first(list, &id_cache);
151 clients[i++] = id_cache->context;
153 if (!silc_idcache_list_next(list, &id_cache))
157 /* Check multiple cache entries for match */
158 silc_idcache_list_first(list, &id_cache);
160 entry = (SilcClientEntry)id_cache->context;
161 if (strcasecmp(entry->nickname, format)) {
162 if (!silc_idcache_list_next(list, &id_cache)) {
169 clients[i++] = id_cache->context;
171 if (!silc_idcache_list_next(list, &id_cache))
177 silc_idcache_list_free(list);
191 SilcClientConnection conn;
193 SilcBuffer client_id_list;
194 SilcGetClientCallback completion;
197 } *GetClientsByListInternal;
199 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
201 GetClientsByListInternal i = (GetClientsByListInternal)context;
202 SilcIDCacheEntry id_cache = NULL;
203 SilcBuffer client_id_list = i->client_id_list;
204 SilcClientEntry *clients = NULL;
205 uint32 clients_count = 0;
208 for (c = 0; c < i->list_count; c++) {
210 SilcClientID *client_id;
213 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
215 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
219 /* Get the client entry */
220 if (silc_idcache_find_by_id_one_ext(i->conn->client_cache,
223 silc_hash_client_id_compare, NULL,
225 clients = silc_realloc(clients, sizeof(*clients) *
226 (clients_count + 1));
227 clients[clients_count] = (SilcClientEntry)id_cache->context;
232 silc_free(client_id);
233 silc_buffer_pull(client_id_list, idp_len);
237 i->completion(i->client, i->conn, clients, clients_count, i->context);
242 static void silc_client_get_clients_list_destructor(void *context)
244 GetClientsByListInternal i = (GetClientsByListInternal)context;
246 if (i->found == FALSE)
247 i->completion(i->client, i->conn, NULL, 0, i->context);
249 if (i->client_id_list)
250 silc_buffer_free(i->client_id_list);
254 /* Gets client entries by the list of client ID's `client_id_list'. This
255 always resolves those client ID's it does not know yet from the server
256 so this function might take a while. The `client_id_list' is a list
257 of ID Payloads added one after other. JOIN command reply and USERS
258 command reply for example returns this sort of list. The `completion'
259 will be called after the entries are available. */
261 void silc_client_get_clients_by_list(SilcClient client,
262 SilcClientConnection conn,
264 SilcBuffer client_id_list,
265 SilcGetClientCallback completion,
268 SilcIDCacheEntry id_cache = NULL;
270 unsigned char **res_argv = NULL;
271 uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
272 GetClientsByListInternal in;
274 in = silc_calloc(1, sizeof(*in));
277 in->list_count = list_count;
278 in->client_id_list = silc_buffer_copy(client_id_list);
279 in->completion = completion;
280 in->context = context;
282 for (i = 0; i < list_count; i++) {
284 SilcClientID *client_id;
285 SilcClientEntry entry;
288 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
290 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
294 /* Check if we have this client cached already. */
296 silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
298 silc_hash_client_id_compare, NULL,
301 /* If we don't have the entry or it has incomplete info, then resolve
302 it from the server. */
303 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
304 if (!id_cache || !entry->nickname) {
305 /* No we don't have it, query it from the server. Assemble argument
306 table that will be sent fr the IDENTIFY command later. */
307 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
309 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
311 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
313 res_argv[res_argc] = client_id_list->data;
314 res_argv_lens[res_argc] = idp_len;
315 res_argv_types[res_argc] = res_argc + 5;
319 silc_free(client_id);
320 silc_buffer_pull(client_id_list, idp_len);
323 /* Query the client information from server if the list included clients
324 that we don't know about. */
328 /* Send the IDENTIFY command to server */
329 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
330 res_argc, res_argv, res_argv_lens,
331 res_argv_types, ++conn->cmd_ident);
332 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
333 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
336 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
338 silc_client_get_clients_list_destructor,
339 silc_client_command_get_clients_list_callback,
342 silc_buffer_push(client_id_list, client_id_list->data -
343 client_id_list->head);
344 silc_buffer_free(res_cmd);
346 silc_free(res_argv_lens);
347 silc_free(res_argv_types);
351 silc_buffer_push(client_id_list, client_id_list->data -
352 client_id_list->head);
354 /* We have the clients in cache, get them and call the completion */
355 silc_client_command_get_clients_list_callback((void *)in, NULL);
358 /* The old style function to find client entry. This is used by the
359 library internally. If `query' is TRUE then the client information is
360 requested by the server. The pending command callback must be set
363 SilcClientEntry silc_idlist_get_client(SilcClient client,
364 SilcClientConnection conn,
365 const char *nickname,
369 SilcIDCacheEntry id_cache;
370 SilcIDCacheList list = NULL;
371 SilcClientEntry entry = NULL;
373 /* Find ID from cache */
374 if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname,
380 SilcClientCommandContext ctx;
382 SILC_LOG_DEBUG(("Requesting Client ID from server"));
384 /* No ID found. Do query from the server. The query is done by
385 sending simple IDENTIFY command to the server. */
386 ctx = silc_client_command_alloc();
387 ctx->client = client;
389 ctx->command = silc_client_command_find("IDENTIFY");
390 memset(ident, 0, sizeof(ident));
391 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
392 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
393 &ctx->argv_types, &ctx->argc, 2);
394 ctx->command->cb(ctx, NULL);
397 silc_idcache_list_free(list);
405 /* Take first found cache entry */
406 if (!silc_idcache_list_first(list, &id_cache))
409 entry = (SilcClientEntry)id_cache->context;
411 /* Check multiple cache entries for match */
412 silc_idcache_list_first(list, &id_cache);
414 entry = (SilcClientEntry)id_cache->context;
416 if (strcasecmp(entry->nickname, format)) {
417 if (!silc_idcache_list_next(list, &id_cache)) {
429 /* If match weren't found, request it */
435 silc_idcache_list_free(list);
440 /* Finds entry for client by the client's ID. Returns the entry or NULL
441 if the entry was not found. */
443 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
444 SilcClientConnection conn,
445 SilcClientID *client_id)
447 SilcIDCacheEntry id_cache;
449 SILC_LOG_DEBUG(("Finding client by ID (%s)",
450 silc_id_render(client_id, SILC_ID_CLIENT)));
452 /* Find ID from cache */
453 if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
455 silc_hash_client_id_compare, NULL,
459 SILC_LOG_DEBUG(("Found"));
461 return (SilcClientEntry)id_cache->context;
466 SilcClientConnection conn;
467 SilcClientID *client_id;
468 SilcGetClientCallback completion;
471 } *GetClientByIDInternal;
473 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
475 GetClientByIDInternal i = (GetClientByIDInternal)context;
476 SilcClientEntry entry;
479 entry = silc_client_get_client_by_id(i->client, i->conn,
482 i->completion(i->client, i->conn, &entry, 1, i->context);
487 static void silc_client_get_client_by_id_destructor(void *context)
489 GetClientByIDInternal i = (GetClientByIDInternal)context;
491 if (i->found == FALSE)
492 i->completion(i->client, i->conn, NULL, 0, i->context);
495 silc_free(i->client_id);
499 /* Same as above but will always resolve the information from the server.
500 Use this only if you know that you don't have the entry and the only
501 thing you know about the client is its ID. */
503 void silc_client_get_client_by_id_resolve(SilcClient client,
504 SilcClientConnection conn,
505 SilcClientID *client_id,
506 SilcGetClientCallback completion,
510 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
512 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
513 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
515 1, 3, idp->data, idp->len);
516 silc_buffer_free(idp);
520 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
521 i->completion = completion;
522 i->context = context;
524 /* Add pending callback */
525 silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
527 silc_client_get_client_by_id_destructor,
528 silc_client_command_get_client_by_id_callback,
532 /* Creates new client entry and adds it to the ID cache. Returns pointer
536 silc_client_add_client(SilcClient client, SilcClientConnection conn,
537 char *nickname, char *username,
538 char *userinfo, SilcClientID *id, uint32 mode)
540 SilcClientEntry client_entry;
543 /* Save the client infos */
544 client_entry = silc_calloc(1, sizeof(*client_entry));
545 client_entry->id = id;
546 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
547 silc_parse_userfqdn(username, &client_entry->username,
548 &client_entry->hostname);
550 client_entry->realname = strdup(userinfo);
551 client_entry->mode = mode;
553 client_entry->nickname = strdup(nick);
555 /* Format the nickname */
556 silc_client_nickname_format(client, conn, client_entry);
558 /* Add client to cache, the non-formatted nickname is saved to cache */
559 if (!silc_idcache_add(conn->client_cache, nick, client_entry->id,
560 (void *)client_entry, FALSE)) {
561 silc_free(client_entry->nickname);
562 silc_free(client_entry->username);
563 silc_free(client_entry->hostname);
564 silc_free(client_entry->server);
565 silc_free(client_entry);
572 /* Updates the `client_entry' with the new information sent as argument. */
574 void silc_client_update_client(SilcClient client,
575 SilcClientConnection conn,
576 SilcClientEntry client_entry,
577 const char *nickname,
578 const char *username,
579 const char *userinfo,
584 if (!client_entry->username && username)
585 silc_parse_userfqdn(username, &client_entry->username,
586 &client_entry->hostname);
587 if (!client_entry->realname && userinfo)
588 client_entry->realname = strdup(userinfo);
589 if (!client_entry->nickname && nickname) {
590 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
591 client_entry->nickname = strdup(nick);
592 silc_client_nickname_format(client, conn, client_entry);
594 client_entry->mode = mode;
597 /* Remove the old cache entry and create a new one */
598 silc_idcache_del_by_context(conn->client_cache, client_entry);
599 silc_idcache_add(conn->client_cache, nick, client_entry->id,
600 client_entry, FALSE);
604 /* Deletes the client entry and frees all memory. */
606 void silc_client_del_client_entry(SilcClient client,
607 SilcClientEntry client_entry)
609 silc_free(client_entry->nickname);
610 silc_free(client_entry->username);
611 silc_free(client_entry->realname);
612 silc_free(client_entry->server);
613 silc_free(client_entry->id);
614 if (client_entry->send_key)
615 silc_cipher_free(client_entry->send_key);
616 if (client_entry->receive_key)
617 silc_cipher_free(client_entry->receive_key);
618 silc_free(client_entry->key);
619 silc_free(client_entry);
622 /* Removes client from the cache by the client entry. */
624 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
625 SilcClientEntry client_entry)
627 bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
628 silc_client_del_client_entry(client, client_entry);
632 /* Removes channel from the cache by the channel entry. */
634 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
635 SilcChannelEntry channel)
637 bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
638 silc_free(channel->channel_name);
639 silc_free(channel->id);
640 silc_free(channel->key);
641 if (channel->channel_key)
642 silc_cipher_free(channel->channel_key);
644 silc_hmac_free(channel->hmac);
645 if (channel->old_channel_key)
646 silc_cipher_free(channel->old_channel_key);
647 if (channel->old_hmac)
648 silc_hmac_free(channel->old_hmac);
649 if (channel->rekey_task)
650 silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
651 silc_client_del_channel_private_keys(client, conn, channel);
656 /* Finds entry for channel by the channel name. Returns the entry or NULL
657 if the entry was not found. It is found only if the client is joined
660 SilcChannelEntry silc_client_get_channel(SilcClient client,
661 SilcClientConnection conn,
664 SilcIDCacheEntry id_cache;
665 SilcChannelEntry entry;
667 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
671 entry = (SilcChannelEntry)id_cache->context;
676 /* Finds entry for channel by the channel ID. Returns the entry or NULL
677 if the entry was not found. It is found only if the client is joined
680 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
681 SilcClientConnection conn,
682 SilcChannelID *channel_id)
684 SilcIDCacheEntry id_cache;
685 SilcChannelEntry entry;
687 if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id,
691 entry = (SilcChannelEntry)id_cache->context;
698 SilcClientConnection conn;
699 SilcChannelID *channel_id;
700 SilcGetChannelCallback completion;
703 } *GetChannelByIDInternal;
705 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
707 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
708 SilcChannelEntry entry;
710 /* Get the channel */
711 entry = silc_client_get_channel_by_id(i->client, i->conn,
714 i->completion(i->client, i->conn, &entry, 1, i->context);
719 static void silc_client_get_channel_by_id_destructor(void *context)
721 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
723 if (i->found == FALSE)
724 i->completion(i->client, i->conn, NULL, 0, i->context);
726 silc_free(i->channel_id);
730 /* Resolves channel information from the server by the channel ID. */
732 void silc_client_get_channel_by_id_resolve(SilcClient client,
733 SilcClientConnection conn,
734 SilcChannelID *channel_id,
735 SilcGetChannelCallback completion,
739 GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
741 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
742 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
744 1, 5, idp->data, idp->len);
745 silc_buffer_free(idp);
749 i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
750 i->completion = completion;
751 i->context = context;
753 /* Add pending callback */
754 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
756 silc_client_get_channel_by_id_destructor,
757 silc_client_command_get_channel_by_id_callback,
761 /* Find channel entry by ID. This routine is used internally by the library. */
763 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
764 SilcClientConnection conn,
765 SilcChannelID *channel_id,
769 SilcChannelEntry channel;
771 channel = silc_client_get_channel_by_id(client, conn, channel_id);
776 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
777 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
779 1, 5, idp->data, idp->len);
780 silc_buffer_free(idp);
786 /* Finds entry for server by the server name. */
788 SilcServerEntry silc_client_get_server(SilcClient client,
789 SilcClientConnection conn,
792 SilcIDCacheEntry id_cache;
793 SilcServerEntry entry;
795 if (!silc_idcache_find_by_name_one(conn->server_cache, server_name,
799 entry = (SilcServerEntry)id_cache->context;
804 /* Finds entry for server by the server ID. */
806 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
807 SilcClientConnection conn,
808 SilcServerID *server_id)
810 SilcIDCacheEntry id_cache;
811 SilcServerEntry entry;
813 if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
817 entry = (SilcServerEntry)id_cache->context;
822 /* Removes server from the cache by the server entry. */
824 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
825 SilcServerEntry server)
827 bool ret = silc_idcache_del_by_context(conn->server_cache, server);
828 silc_free(server->server_name);
829 silc_free(server->server_info);
830 silc_free(server->server_id);
835 /* Formats the nickname of the client specified by the `client_entry'.
836 If the format is specified by the application this will format the
837 nickname and replace the old nickname in the client entry. If the
838 format string is not specified then this function has no effect. */
840 void silc_client_nickname_format(SilcClient client,
841 SilcClientConnection conn,
842 SilcClientEntry client_entry)
845 char *newnick = NULL;
847 SilcClientEntry *clients;
848 uint32 clients_count;
850 if (!client->params->nickname_format[0])
853 if (!client_entry->nickname)
856 /* Get all clients with same nickname. Do not perform the formatting
857 if there aren't any clients with same nickname unless the application
858 is forcing us to do so. */
859 clients = silc_client_get_clients_local(client, conn,
860 client_entry->nickname, NULL,
862 if (!clients && !client->params->nickname_force_format)
865 cp = client->params->nickname_format;
875 if (!client_entry->nickname)
877 len = strlen(client_entry->nickname);
878 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
879 memcpy(&newnick[off], client_entry->nickname, len);
883 /* Stripped hostname */
884 if (!client_entry->hostname)
886 len = strcspn(client_entry->hostname, ".");
887 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
888 memcpy(&newnick[off], client_entry->hostname, len);
893 if (!client_entry->hostname)
895 len = strlen(client_entry->hostname);
896 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
897 memcpy(&newnick[off], client_entry->hostname, len);
901 /* Stripped server name */
902 if (!client_entry->server)
904 len = strcspn(client_entry->server, ".");
905 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
906 memcpy(&newnick[off], client_entry->server, len);
910 /* Full server name */
911 if (!client_entry->server)
913 len = strlen(client_entry->server);
914 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
915 memcpy(&newnick[off], client_entry->server, len);
919 /* Ascending number */
924 if (clients_count == 1)
927 for (i = 0; i < clients_count; i++) {
928 if (strncmp(clients[i]->nickname, newnick, off))
930 if (strlen(clients[i]->nickname) <= off)
932 num = atoi(&clients[i]->nickname[off]);
937 memset(tmp, 0, sizeof(tmp));
938 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
940 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
941 memcpy(&newnick[off], tmp, len);
946 /* Some other character in the string */
947 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
948 memcpy(&newnick[off], cp, 1);
956 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
959 silc_free(client_entry->nickname);
960 client_entry->nickname = newnick;