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;
37 unsigned int clients_count;
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 unsigned int *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_data_loose(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;
176 unsigned int list_count;
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 unsigned int clients_count = 0;
192 for (c = 0; c < i->list_count; c++) {
193 unsigned short idp_len;
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(i->conn->client_cache, (void *)client_id,
205 SILC_ID_CLIENT, &id_cache)) {
206 clients = silc_realloc(clients, sizeof(*clients) *
207 (clients_count + 1));
208 clients[clients_count] = (SilcClientEntry)id_cache->context;
213 silc_free(client_id);
214 silc_buffer_pull(client_id_list, idp_len);
218 i->completion(i->client, i->conn, clients, clients_count, i->context);
223 static void silc_client_get_clients_list_destructor(void *context)
225 GetClientsByListInternal i = (GetClientsByListInternal)context;
227 if (i->found == FALSE)
228 i->completion(i->client, i->conn, NULL, 0, i->context);
230 if (i->client_id_list)
231 silc_buffer_free(i->client_id_list);
235 /* Gets client entries by the list of client ID's `client_id_list'. This
236 always resolves those client ID's it does not know yet from the server
237 so this function might take a while. The `client_id_list' is a list
238 of ID Payloads added one after other. JOIN command reply and USERS
239 command reply for example returns this sort of list. The `completion'
240 will be called after the entries are available. */
242 void silc_client_get_clients_by_list(SilcClient client,
243 SilcClientConnection conn,
244 unsigned int list_count,
245 SilcBuffer client_id_list,
246 SilcGetClientCallback completion,
249 SilcIDCacheEntry id_cache = NULL;
251 unsigned char **res_argv = NULL;
252 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
253 GetClientsByListInternal in;
255 in = silc_calloc(1, sizeof(*in));
258 in->list_count = list_count;
259 in->client_id_list = silc_buffer_copy(client_id_list);
260 in->completion = completion;
261 in->context = context;
263 for (i = 0; i < list_count; i++) {
264 unsigned short idp_len;
265 SilcClientID *client_id;
266 SilcClientEntry entry;
269 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
271 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
275 /* Check if we have this client cached already. */
277 silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
278 SILC_ID_CLIENT, &id_cache);
280 /* If we don't have the entry or it has incomplete info, then resolve
281 it from the server. */
282 entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
283 if (!id_cache || !entry->nickname) {
284 /* No we don't have it, query it from the server. Assemble argument
285 table that will be sent fr the IDENTIFY command later. */
286 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
288 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
290 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
292 res_argv[res_argc] = client_id_list->data;
293 res_argv_lens[res_argc] = idp_len;
294 res_argv_types[res_argc] = res_argc + 3;
298 silc_free(client_id);
299 silc_buffer_pull(client_id_list, idp_len);
301 silc_buffer_push(client_id_list, client_id_list->data -
302 client_id_list->head);
304 /* Query the client information from server if the list included clients
305 that we don't know about. */
309 /* Send the IDENTIFY command to server */
310 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
311 res_argc, res_argv, res_argv_lens,
312 res_argv_types, ++conn->cmd_ident);
313 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
314 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
317 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
319 silc_client_get_clients_list_destructor,
320 silc_client_command_get_clients_list_callback,
323 silc_buffer_free(res_cmd);
325 silc_free(res_argv_lens);
326 silc_free(res_argv_types);
330 /* We have the clients in cache, get them and call the completion */
331 silc_client_command_get_clients_list_callback((void *)in);
334 /* The old style function to find client entry. This is used by the
335 library internally. If `query' is TRUE then the client information is
336 requested by the server. The pending command callback must be set
339 SilcClientEntry silc_idlist_get_client(SilcClient client,
340 SilcClientConnection conn,
346 SilcIDCacheEntry id_cache;
347 SilcIDCacheList list = NULL;
348 SilcClientEntry entry = NULL;
350 /* Find ID from cache */
351 if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list)) {
356 SilcClientCommandContext ctx;
358 SILC_LOG_DEBUG(("Requesting Client ID from server"));
360 /* No ID found. Do query from the server. The query is done by
361 sending simple IDENTIFY command to the server. */
362 ctx = silc_client_command_alloc();
363 ctx->client = client;
365 ctx->command = silc_client_command_find("IDENTIFY");
366 memset(ident, 0, sizeof(ident));
367 snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
368 silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
369 &ctx->argv_types, &ctx->argc, 2);
370 ctx->command->cb(ctx);
373 silc_idcache_list_free(list);
380 if (!server && !num) {
381 /* Take first found cache entry */
382 if (!silc_idcache_list_first(list, &id_cache))
385 entry = (SilcClientEntry)id_cache->context;
387 /* Check multiple cache entries for match */
388 silc_idcache_list_first(list, &id_cache);
389 entry = (SilcClientEntry)id_cache->context;
392 if (server && entry->server &&
393 !strncasecmp(server, entry->server, strlen(server)))
396 if (num && entry->num == num)
399 if (!silc_idcache_list_next(list, &id_cache)) {
404 entry = (SilcClientEntry)id_cache->context;
407 /* If match weren't found, request it */
413 silc_idcache_list_free(list);
418 /* Finds entry for client by the client's ID. Returns the entry or NULL
419 if the entry was not found. */
421 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
422 SilcClientConnection conn,
423 SilcClientID *client_id)
425 SilcIDCacheEntry id_cache;
427 SILC_LOG_DEBUG(("Finding client by ID (%s)",
428 silc_id_render(client_id, SILC_ID_CLIENT)));
430 /* Find ID from cache */
431 if (!silc_idcache_find_by_id_one(conn->client_cache, client_id,
432 SILC_ID_CLIENT, &id_cache))
435 SILC_LOG_DEBUG(("Found"));
437 return (SilcClientEntry)id_cache->context;
442 SilcClientConnection conn;
443 SilcClientID *client_id;
444 SilcGetClientCallback completion;
447 } *GetClientByIDInternal;
449 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
451 GetClientByIDInternal i = (GetClientByIDInternal)context;
452 SilcClientEntry entry;
455 entry = silc_client_get_client_by_id(i->client, i->conn,
458 i->completion(i->client, i->conn, &entry, 1, i->context);
463 static void silc_client_get_client_by_id_destructor(void *context)
465 GetClientByIDInternal i = (GetClientByIDInternal)context;
467 if (i->found == FALSE)
468 i->completion(i->client, i->conn, NULL, 0, i->context);
471 silc_free(i->client_id);
475 /* Same as above but will always resolve the information from the server.
476 Use this only if you know that you don't have the entry and the only
477 thing you know about the client is its ID. */
479 void silc_client_get_client_by_id_resolve(SilcClient client,
480 SilcClientConnection conn,
481 SilcClientID *client_id,
482 SilcGetClientCallback completion,
486 GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
488 idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
489 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS,
491 1, 3, idp->data, idp->len);
492 silc_buffer_free(idp);
496 i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
497 i->completion = completion;
498 i->context = context;
500 /* Add pending callback */
501 silc_client_command_pending(conn, SILC_COMMAND_WHOIS,
503 silc_client_get_client_by_id_destructor,
504 silc_client_command_get_client_by_id_callback,
508 /* Finds entry for channel by the channel name. Returns the entry or NULL
509 if the entry was not found. It is found only if the client is joined
512 SilcChannelEntry silc_client_get_channel(SilcClient client,
513 SilcClientConnection conn,
516 SilcIDCacheEntry id_cache;
517 SilcChannelEntry entry;
519 if (!silc_idcache_find_by_data_one(conn->channel_cache, channel, &id_cache))
522 entry = (SilcChannelEntry)id_cache->context;