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 client_entry->valid = TRUE;
547 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
548 silc_parse_userfqdn(username, &client_entry->username,
549 &client_entry->hostname);
551 client_entry->realname = strdup(userinfo);
552 client_entry->mode = mode;
554 client_entry->nickname = strdup(nick);
556 /* Format the nickname */
557 silc_client_nickname_format(client, conn, client_entry);
559 /* Add client to cache, the non-formatted nickname is saved to cache */
560 if (!silc_idcache_add(conn->client_cache, nick, client_entry->id,
561 (void *)client_entry, FALSE)) {
562 silc_free(client_entry->nickname);
563 silc_free(client_entry->username);
564 silc_free(client_entry->hostname);
565 silc_free(client_entry->server);
566 silc_free(client_entry);
573 /* Updates the `client_entry' with the new information sent as argument. */
575 void silc_client_update_client(SilcClient client,
576 SilcClientConnection conn,
577 SilcClientEntry client_entry,
578 const char *nickname,
579 const char *username,
580 const char *userinfo,
585 if (!client_entry->username && username)
586 silc_parse_userfqdn(username, &client_entry->username,
587 &client_entry->hostname);
588 if (!client_entry->realname && userinfo)
589 client_entry->realname = strdup(userinfo);
590 if (!client_entry->nickname && nickname) {
591 silc_parse_userfqdn(nickname, &nick, &client_entry->server);
592 client_entry->nickname = strdup(nick);
593 silc_client_nickname_format(client, conn, client_entry);
595 client_entry->mode = mode;
598 /* Remove the old cache entry and create a new one */
599 silc_idcache_del_by_context(conn->client_cache, client_entry);
600 silc_idcache_add(conn->client_cache, nick, client_entry->id,
601 client_entry, FALSE);
605 /* Deletes the client entry and frees all memory. */
607 void silc_client_del_client_entry(SilcClient client,
608 SilcClientEntry client_entry)
610 silc_free(client_entry->nickname);
611 silc_free(client_entry->username);
612 silc_free(client_entry->realname);
613 silc_free(client_entry->server);
614 silc_free(client_entry->id);
615 if (client_entry->send_key)
616 silc_cipher_free(client_entry->send_key);
617 if (client_entry->receive_key)
618 silc_cipher_free(client_entry->receive_key);
619 silc_free(client_entry->key);
620 silc_free(client_entry);
623 /* Removes client from the cache by the client entry. */
625 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
626 SilcClientEntry client_entry)
628 bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
629 silc_client_del_client_entry(client, client_entry);
633 /* Removes channel from the cache by the channel entry. */
635 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
636 SilcChannelEntry channel)
638 bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
639 silc_free(channel->channel_name);
640 silc_free(channel->id);
641 silc_free(channel->key);
642 if (channel->channel_key)
643 silc_cipher_free(channel->channel_key);
645 silc_hmac_free(channel->hmac);
646 if (channel->old_channel_key)
647 silc_cipher_free(channel->old_channel_key);
648 if (channel->old_hmac)
649 silc_hmac_free(channel->old_hmac);
650 if (channel->rekey_task)
651 silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
652 silc_client_del_channel_private_keys(client, conn, channel);
657 /* Finds entry for channel by the channel name. Returns the entry or NULL
658 if the entry was not found. It is found only if the client is joined
661 SilcChannelEntry silc_client_get_channel(SilcClient client,
662 SilcClientConnection conn,
665 SilcIDCacheEntry id_cache;
666 SilcChannelEntry entry;
668 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
672 entry = (SilcChannelEntry)id_cache->context;
677 /* Finds entry for channel by the channel ID. Returns the entry or NULL
678 if the entry was not found. It is found only if the client is joined
681 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
682 SilcClientConnection conn,
683 SilcChannelID *channel_id)
685 SilcIDCacheEntry id_cache;
686 SilcChannelEntry entry;
688 if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id,
692 entry = (SilcChannelEntry)id_cache->context;
699 SilcClientConnection conn;
700 SilcChannelID *channel_id;
701 SilcGetChannelCallback completion;
704 } *GetChannelByIDInternal;
706 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
708 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
709 SilcChannelEntry entry;
711 /* Get the channel */
712 entry = silc_client_get_channel_by_id(i->client, i->conn,
715 i->completion(i->client, i->conn, &entry, 1, i->context);
720 static void silc_client_get_channel_by_id_destructor(void *context)
722 GetChannelByIDInternal i = (GetChannelByIDInternal)context;
724 if (i->found == FALSE)
725 i->completion(i->client, i->conn, NULL, 0, i->context);
727 silc_free(i->channel_id);
731 /* Resolves channel information from the server by the channel ID. */
733 void silc_client_get_channel_by_id_resolve(SilcClient client,
734 SilcClientConnection conn,
735 SilcChannelID *channel_id,
736 SilcGetChannelCallback completion,
740 GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
742 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
743 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
745 1, 5, idp->data, idp->len);
746 silc_buffer_free(idp);
750 i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
751 i->completion = completion;
752 i->context = context;
754 /* Add pending callback */
755 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
757 silc_client_get_channel_by_id_destructor,
758 silc_client_command_get_channel_by_id_callback,
762 /* Find channel entry by ID. This routine is used internally by the library. */
764 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
765 SilcClientConnection conn,
766 SilcChannelID *channel_id,
770 SilcChannelEntry channel;
772 channel = silc_client_get_channel_by_id(client, conn, channel_id);
777 idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
778 silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY,
780 1, 5, idp->data, idp->len);
781 silc_buffer_free(idp);
787 /* Finds entry for server by the server name. */
789 SilcServerEntry silc_client_get_server(SilcClient client,
790 SilcClientConnection conn,
793 SilcIDCacheEntry id_cache;
794 SilcServerEntry entry;
796 if (!silc_idcache_find_by_name_one(conn->server_cache, server_name,
800 entry = (SilcServerEntry)id_cache->context;
805 /* Finds entry for server by the server ID. */
807 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
808 SilcClientConnection conn,
809 SilcServerID *server_id)
811 SilcIDCacheEntry id_cache;
812 SilcServerEntry entry;
814 if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
818 entry = (SilcServerEntry)id_cache->context;
823 /* Removes server from the cache by the server entry. */
825 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
826 SilcServerEntry server)
828 bool ret = silc_idcache_del_by_context(conn->server_cache, server);
829 silc_free(server->server_name);
830 silc_free(server->server_info);
831 silc_free(server->server_id);
836 /* Formats the nickname of the client specified by the `client_entry'.
837 If the format is specified by the application this will format the
838 nickname and replace the old nickname in the client entry. If the
839 format string is not specified then this function has no effect. */
841 void silc_client_nickname_format(SilcClient client,
842 SilcClientConnection conn,
843 SilcClientEntry client_entry)
846 char *newnick = NULL;
848 SilcClientEntry *clients;
849 uint32 clients_count = 0;
851 if (!client->params->nickname_format[0])
854 if (!client_entry->nickname)
857 /* Get all clients with same nickname. Do not perform the formatting
858 if there aren't any clients with same nickname unless the application
859 is forcing us to do so. */
860 clients = silc_client_get_clients_local(client, conn,
861 client_entry->nickname, NULL,
863 if (!clients && !client->params->nickname_force_format)
867 for (i = 0; i < clients_count; i++)
868 if (clients[i]->valid)
873 cp = client->params->nickname_format;
883 if (!client_entry->nickname)
885 len = strlen(client_entry->nickname);
886 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
887 memcpy(&newnick[off], client_entry->nickname, len);
891 /* Stripped hostname */
892 if (!client_entry->hostname)
894 len = strcspn(client_entry->hostname, ".");
895 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
896 memcpy(&newnick[off], client_entry->hostname, len);
901 if (!client_entry->hostname)
903 len = strlen(client_entry->hostname);
904 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
905 memcpy(&newnick[off], client_entry->hostname, len);
909 /* Stripped server name */
910 if (!client_entry->server)
912 len = strcspn(client_entry->server, ".");
913 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
914 memcpy(&newnick[off], client_entry->server, len);
918 /* Full server name */
919 if (!client_entry->server)
921 len = strlen(client_entry->server);
922 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
923 memcpy(&newnick[off], client_entry->server, len);
927 /* Ascending number */
932 if (clients_count == 1)
935 for (i = 0; i < clients_count; i++) {
936 if (strncasecmp(clients[i]->nickname, newnick, off))
938 if (strlen(clients[i]->nickname) <= off)
940 num = atoi(&clients[i]->nickname[off]);
945 memset(tmp, 0, sizeof(tmp));
946 snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
948 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
949 memcpy(&newnick[off], tmp, len);
954 /* Some other character in the string */
955 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
956 memcpy(&newnick[off], cp, 1);
964 newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
967 silc_free(client_entry->nickname);
968 client_entry->nickname = newnick;