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, &list))
130 if (!silc_idcache_list_count(list)) {
131 silc_idcache_list_free(list);
135 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
136 *clients_count = silc_idcache_list_count(list);
139 /* Take all without any further checking */
140 silc_idcache_list_first(list, &id_cache);
142 clients[i++] = id_cache->context;
143 if (!silc_idcache_list_next(list, &id_cache))
147 /* Check multiple cache entries for match */
148 silc_idcache_list_first(list, &id_cache);
150 entry = (SilcClientEntry)id_cache->context;
153 strncasecmp(server, entry->server, strlen(server))) {
154 if (!silc_idcache_list_next(list, &id_cache)) {
161 clients[i++] = id_cache->context;
162 if (!silc_idcache_list_next(list, &id_cache))
168 silc_idcache_list_free(list);
175 SilcClientConnection conn;
177 SilcBuffer client_id_list;
178 SilcGetClientCallback completion;
181 } *GetClientsByListInternal;
183 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
185 GetClientsByListInternal i = (GetClientsByListInternal)context;
186 SilcIDCacheEntry id_cache = NULL;
187 SilcBuffer client_id_list = i->client_id_list;
188 SilcClientEntry *clients = NULL;
189 uint32 clients_count = 0;
192 for (c = 0; c < i->list_count; c++) {
194 SilcClientID *client_id;
197 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
199 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
203 /* Get the client entry */
204 if (silc_idcache_find_by_id_one_ext(i->conn->client_cache,
207 silc_hash_client_id_compare, NULL,
209 clients = silc_realloc(clients, sizeof(*clients) *
210 (clients_count + 1));
211 clients[clients_count] = (SilcClientEntry)id_cache->context;
216 silc_free(client_id);
217 silc_buffer_pull(client_id_list, idp_len);
221 i->completion(i->client, i->conn, clients, clients_count, i->context);
226 static void silc_client_get_clients_list_destructor(void *context)
228 GetClientsByListInternal i = (GetClientsByListInternal)context;
230 if (i->found == FALSE)
231 i->completion(i->client, i->conn, NULL, 0, i->context);
233 if (i->client_id_list)
234 silc_buffer_free(i->client_id_list);
238 /* Gets client entries by the list of client ID's `client_id_list'. This
239 always resolves those client ID's it does not know yet from the server
240 so this function might take a while. The `client_id_list' is a list
241 of ID Payloads added one after other. JOIN command reply and USERS
242 command reply for example returns this sort of list. The `completion'
243 will be called after the entries are available. */
245 void silc_client_get_clients_by_list(SilcClient client,
246 SilcClientConnection conn,
248 SilcBuffer client_id_list,
249 SilcGetClientCallback completion,
252 SilcIDCacheEntry id_cache = NULL;
254 unsigned char **res_argv = NULL;
255 uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
256 GetClientsByListInternal in;
258 in = silc_calloc(1, sizeof(*in));
261 in->list_count = list_count;
262 in->client_id_list = silc_buffer_copy(client_id_list);
263 in->completion = completion;
264 in->context = context;
266 for (i = 0; i < list_count; i++) {
268 SilcClientID *client_id;
269 SilcClientEntry entry;
272 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
274 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
278 /* Check if we have this client cached already. */
280 silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
282 silc_hash_client_id_compare, NULL,
285 /* If we don't have the entry or it has incomplete info, then resolve
286 it from the server. */
287 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
288 if (!id_cache || !entry->nickname) {
289 /* No we don't have it, query it from the server. Assemble argument
290 table that will be sent fr the IDENTIFY command later. */
291 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
293 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
295 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
297 res_argv[res_argc] = client_id_list->data;
298 res_argv_lens[res_argc] = idp_len;
299 res_argv_types[res_argc] = res_argc + 3;
303 silc_free(client_id);
304 silc_buffer_pull(client_id_list, idp_len);
307 /* Query the client information from server if the list included clients
308 that we don't know about. */
312 /* Send the IDENTIFY command to server */
313 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
314 res_argc, res_argv, res_argv_lens,
315 res_argv_types, ++conn->cmd_ident);
316 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
317 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
320 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
322 silc_client_get_clients_list_destructor,
323 silc_client_command_get_clients_list_callback,
326 silc_buffer_push(client_id_list, client_id_list->data -
327 client_id_list->head);
328 silc_buffer_free(res_cmd);
330 silc_free(res_argv_lens);
331 silc_free(res_argv_types);
335 silc_buffer_push(client_id_list, client_id_list->data -
336 client_id_list->head);
338 /* We have the clients in cache, get them and call the completion */
339 silc_client_command_get_clients_list_callback((void *)in);
342 /* The old style function to find client entry. This is used by the
343 library internally. If `query' is TRUE then the client information is
344 requested by the server. The pending command callback must be set
347 SilcClientEntry silc_idlist_get_client(SilcClient client,
348 SilcClientConnection conn,
354 SilcIDCacheEntry id_cache;
355 SilcIDCacheList list = NULL;
356 SilcClientEntry entry = NULL;
358 /* Find ID from cache */
359 if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
364 SilcClientCommandContext ctx;
366 SILC_LOG_DEBUG(("Requesting Client ID from server"));
368 /* No ID found. Do query from the server. The query is done by
369 sending simple IDENTIFY command to the server. */
370 ctx = silc_client_command_alloc();
371 ctx->client = client;
373 ctx->command = silc_client_command_find("IDENTIFY");
374 memset(ident, 0, sizeof(ident));
375 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
376 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
377 &ctx->argv_types, &ctx->argc, 2);
378 ctx->command->cb(ctx);
381 silc_idcache_list_free(list);
388 if (!server && !num) {
389 /* Take first found cache entry */
390 if (!silc_idcache_list_first(list, &id_cache))
393 entry = (SilcClientEntry)id_cache->context;
395 /* Check multiple cache entries for match */
396 silc_idcache_list_first(list, &id_cache);
397 entry = (SilcClientEntry)id_cache->context;
400 if (server && entry->server &&
401 !strncasecmp(server, entry->server, strlen(server)))
404 if (num && entry->num == num)
407 if (!silc_idcache_list_next(list, &id_cache)) {
412 entry = (SilcClientEntry)id_cache->context;
415 /* If match weren't found, request it */
421 silc_idcache_list_free(list);
426 /* Finds entry for client by the client's ID. Returns the entry or NULL
427 if the entry was not found. */
429 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
430 SilcClientConnection conn,
431 SilcClientID *client_id)
433 SilcIDCacheEntry id_cache;
435 SILC_LOG_DEBUG(("Finding client by ID (%s)",
436 silc_id_render(client_id, SILC_ID_CLIENT)));
438 /* Find ID from cache */
439 if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id,
441 silc_hash_client_id_compare, NULL,
445 SILC_LOG_DEBUG(("Found"));
447 return (SilcClientEntry)id_cache->context;
452 SilcClientConnection conn;
453 SilcClientID *client_id;
454 SilcGetClientCallback completion;
457 } *GetClientByIDInternal;
459 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
461 GetClientByIDInternal i = (GetClientByIDInternal)context;
462 SilcClientEntry entry;
465 entry = silc_client_get_client_by_id(i->client, i->conn,
468 i->completion(i->client, i->conn, &entry, 1, i->context);
473 static void silc_client_get_client_by_id_destructor(void *context)
475 GetClientByIDInternal i = (GetClientByIDInternal)context;
477 if (i->found == FALSE)
478 i->completion(i->client, i->conn, NULL, 0, i->context);
481 silc_free(i->client_id);
485 /* Same as above but will always resolve the information from the server.
486 Use this only if you know that you don't have the entry and the only
487 thing you know about the client is its ID. */
489 void silc_client_get_client_by_id_resolve(SilcClient client,
490 SilcClientConnection conn,
491 SilcClientID *client_id,
492 SilcGetClientCallback completion,
496 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
498 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
499 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
501 1, 3, idp->data, idp->len);
502 silc_buffer_free(idp);
506 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
507 i->completion = completion;
508 i->context = context;
510 /* Add pending callback */
511 silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
513 silc_client_get_client_by_id_destructor,
514 silc_client_command_get_client_by_id_callback,
518 /* Finds entry for channel by the channel name. Returns the entry or NULL
519 if the entry was not found. It is found only if the client is joined
522 SilcChannelEntry silc_client_get_channel(SilcClient client,
523 SilcClientConnection conn,
526 SilcIDCacheEntry id_cache;
527 SilcChannelEntry entry;
529 if (!silc_idcache_find_by_name_one(conn->channel_cache, channel,
533 entry = (SilcChannelEntry)id_cache->context;