updates.
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 Pekka Riikonen
8
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.
13   
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.
18
19 */
20 /* $Id$ */
21
22 #include "clientlibincludes.h"
23
24 typedef struct {
25   SilcClientCommandContext cmd;
26   SilcGetClientCallback completion;
27   char *nickname;
28   char *server;
29   void *context;
30   int found;
31 } *GetClientInternal;
32
33 SILC_CLIENT_CMD_FUNC(get_client_callback)
34 {
35   GetClientInternal i = (GetClientInternal)context;
36   SilcClientEntry *clients;
37   uint32 clients_count;
38
39   /* Get the clients */
40   clients = silc_client_get_clients_local(i->cmd->client, i->cmd->conn,
41                                           i->nickname, i->server,
42                                           &clients_count);
43   if (clients) {
44     i->completion(i->cmd->client, i->cmd->conn, clients, 
45                   clients_count, i->context);
46     i->found = TRUE;
47     silc_free(clients);
48   }
49 }
50
51 static void silc_client_get_client_destructor(void *context)
52 {
53   GetClientInternal i = (GetClientInternal)context;
54
55   if (i->found == FALSE)
56     i->completion(i->cmd->client, i->cmd->conn, NULL, 0, i->context);
57
58   silc_client_command_free(i->cmd);
59   if (i->nickname)
60     silc_free(i->nickname);
61   if (i->server)
62     silc_free(i->server);
63   silc_free(i);
64 }
65
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.
68
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. */
74
75 void silc_client_get_clients(SilcClient client,
76                              SilcClientConnection conn,
77                              char *nickname,
78                              char *server,
79                              SilcGetClientCallback completion,
80                              void *context)
81 {
82   char ident[512];
83   SilcClientCommandContext ctx;
84   GetClientInternal i = silc_calloc(1, sizeof(*i));
85       
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();
89   ctx->client = client;
90   ctx->conn = conn;
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);
97       
98   i->cmd = ctx;
99   i->nickname = nickname ? strdup(nickname) : NULL;
100   i->server = server ? strdup(server) : NULL;
101   i->completion = completion;
102   i->context = context;
103
104   /* Add pending callback */
105   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
106                               ++conn->cmd_ident, 
107                               silc_client_get_client_destructor,
108                               silc_client_command_get_client_callback, 
109                               (void *)i);
110 }
111
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. */
114
115 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
116                                                SilcClientConnection conn,
117                                                char *nickname,
118                                                char *server,
119                                                uint32 *clients_count)
120 {
121   SilcIDCacheEntry id_cache;
122   SilcIDCacheList list = NULL;
123   SilcClientEntry entry, *clients;
124   int i = 0;
125
126   /* Find ID from cache */
127   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
128     return NULL;
129
130   if (!silc_idcache_list_count(list)) {
131     silc_idcache_list_free(list);
132     return NULL;
133   }
134
135   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
136   *clients_count = silc_idcache_list_count(list);
137
138   if (!server) {
139     /* Take all without any further checking */
140     silc_idcache_list_first(list, &id_cache);
141     while (id_cache) {
142       clients[i++] = id_cache->context;
143       if (!silc_idcache_list_next(list, &id_cache))
144         break;
145     }
146   } else {
147     /* Check multiple cache entries for match */
148     silc_idcache_list_first(list, &id_cache);
149     while (id_cache) {
150       entry = (SilcClientEntry)id_cache->context;
151       
152       if (entry->server && 
153           strncasecmp(server, entry->server, strlen(server))) {
154         if (!silc_idcache_list_next(list, &id_cache)) {
155           break;
156         } else {
157           continue;
158         }
159       }
160       
161       clients[i++] = id_cache->context;
162       if (!silc_idcache_list_next(list, &id_cache))
163         break;
164     }
165   }
166
167   if (list)
168     silc_idcache_list_free(list);
169
170   return clients;
171 }
172
173 typedef struct {
174   SilcClient client;
175   SilcClientConnection conn;
176   uint32 list_count;
177   SilcBuffer client_id_list;
178   SilcGetClientCallback completion;
179   void *context;
180   int found;
181 } *GetClientsByListInternal;
182
183 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
184 {
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;
190   int c;
191
192   for (c = 0; c < i->list_count; c++) {
193     uint16 idp_len;
194     SilcClientID *client_id;
195
196     /* Get Client ID */
197     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
198     idp_len += 4;
199     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
200     if (!client_id)
201       continue;
202
203     /* Get the client entry */
204     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
205                                         (void *)client_id, 
206                                         NULL, NULL, 
207                                         silc_hash_client_id_compare, NULL,
208                                         &id_cache)) {
209       clients = silc_realloc(clients, sizeof(*clients) * 
210                              (clients_count + 1));
211       clients[clients_count] = (SilcClientEntry)id_cache->context;
212       clients_count++;
213       i->found = TRUE;
214     }
215
216     silc_free(client_id);
217     silc_buffer_pull(client_id_list, idp_len);
218   }
219
220   if (i->found) {
221     i->completion(i->client, i->conn, clients, clients_count, i->context);
222     silc_free(clients);
223   }
224 }
225
226 static void silc_client_get_clients_list_destructor(void *context)
227 {
228   GetClientsByListInternal i = (GetClientsByListInternal)context;
229
230   if (i->found == FALSE)
231     i->completion(i->client, i->conn, NULL, 0, i->context);
232
233   if (i->client_id_list)
234     silc_buffer_free(i->client_id_list);
235   silc_free(i);
236 }
237
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. */
244
245 void silc_client_get_clients_by_list(SilcClient client,
246                                      SilcClientConnection conn,
247                                      uint32 list_count,
248                                      SilcBuffer client_id_list,
249                                      SilcGetClientCallback completion,
250                                      void *context)
251 {
252   SilcIDCacheEntry id_cache = NULL;
253   int i;
254   unsigned char **res_argv = NULL;
255   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
256   GetClientsByListInternal in;
257
258   in = silc_calloc(1, sizeof(*in));
259   in->client = client;
260   in->conn = conn;
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;
265
266   for (i = 0; i < list_count; i++) {
267     uint16 idp_len;
268     SilcClientID *client_id;
269     SilcClientEntry entry;
270
271     /* Get Client ID */
272     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
273     idp_len += 4;
274     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
275     if (!client_id)
276       continue;
277
278     /* Check if we have this client cached already. */
279     id_cache = NULL;
280     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
281                                     NULL, NULL, 
282                                     silc_hash_client_id_compare, NULL,
283                                     &id_cache);
284
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) *
292                               (res_argc + 1));
293       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
294                                    (res_argc + 1));
295       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
296                                     (res_argc + 1));
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;
300       res_argc++;
301     }
302
303     silc_free(client_id);
304     silc_buffer_pull(client_id_list, idp_len);
305   }
306
307   /* Query the client information from server if the list included clients
308      that we don't know about. */
309   if (res_argc) {
310     SilcBuffer res_cmd;
311
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,
318                             TRUE);
319
320     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
321                                 conn->cmd_ident, 
322                                 silc_client_get_clients_list_destructor,
323                                 silc_client_command_get_clients_list_callback, 
324                                 (void *)in);
325
326     silc_buffer_push(client_id_list, client_id_list->data - 
327                      client_id_list->head);
328     silc_buffer_free(res_cmd);
329     silc_free(res_argv);
330     silc_free(res_argv_lens);
331     silc_free(res_argv_types);
332     return;
333   }
334
335   silc_buffer_push(client_id_list, client_id_list->data - 
336                    client_id_list->head);
337
338   /* We have the clients in cache, get them and call the completion */
339   silc_client_command_get_clients_list_callback((void *)in);
340 }
341
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
345    by the caller. */
346
347 SilcClientEntry silc_idlist_get_client(SilcClient client,
348                                        SilcClientConnection conn,
349                                        char *nickname,
350                                        char *server,
351                                        uint32 num,
352                                        int query)
353 {
354   SilcIDCacheEntry id_cache;
355   SilcIDCacheList list = NULL;
356   SilcClientEntry entry = NULL;
357
358   /* Find ID from cache */
359   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
360   identify:
361
362     if (query) {
363       char ident[512];
364       SilcClientCommandContext ctx;
365       
366       SILC_LOG_DEBUG(("Requesting Client ID from server"));
367       
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;
372       ctx->conn = conn;
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);
379       
380       if (list)
381         silc_idcache_list_free(list);
382
383       return NULL;
384     }
385     return NULL;
386   }
387
388   if (!server && !num) {
389     /* Take first found cache entry */
390     if (!silc_idcache_list_first(list, &id_cache))
391       goto identify;
392
393     entry = (SilcClientEntry)id_cache->context;
394   } else {
395     /* Check multiple cache entries for match */
396     silc_idcache_list_first(list, &id_cache);
397     entry = (SilcClientEntry)id_cache->context;
398     
399     while (entry) {
400       if (server && entry->server && 
401           !strncasecmp(server, entry->server, strlen(server)))
402         break;
403       
404       if (num && entry->num == num)
405         break;
406
407       if (!silc_idcache_list_next(list, &id_cache)) {
408         entry = NULL;
409         break;
410       }
411
412       entry = (SilcClientEntry)id_cache->context;
413     }
414
415     /* If match weren't found, request it */
416     if (!entry)
417       goto identify;
418   }
419
420   if (list)
421     silc_idcache_list_free(list);
422
423   return entry;
424 }
425
426 /* Finds entry for client by the client's ID. Returns the entry or NULL
427    if the entry was not found. */
428
429 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
430                                              SilcClientConnection conn,
431                                              SilcClientID *client_id)
432 {
433   SilcIDCacheEntry id_cache;
434
435   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
436                   silc_id_render(client_id, SILC_ID_CLIENT)));
437
438   /* Find ID from cache */
439   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
440                                        NULL, NULL, 
441                                        silc_hash_client_id_compare, NULL,
442                                        &id_cache))
443     return NULL;
444
445   SILC_LOG_DEBUG(("Found"));
446
447   return (SilcClientEntry)id_cache->context;
448 }
449
450 typedef struct {
451   SilcClient client;
452   SilcClientConnection conn;
453   SilcClientID *client_id;
454   SilcGetClientCallback completion;
455   void *context;
456   int found;
457 } *GetClientByIDInternal;
458
459 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
460 {
461   GetClientByIDInternal i = (GetClientByIDInternal)context;
462   SilcClientEntry entry;
463
464   /* Get the client */
465   entry = silc_client_get_client_by_id(i->client, i->conn,
466                                        i->client_id);
467   if (entry) {
468     i->completion(i->client, i->conn, &entry, 1, i->context);
469     i->found = TRUE;
470   }
471 }
472
473 static void silc_client_get_client_by_id_destructor(void *context)
474 {
475   GetClientByIDInternal i = (GetClientByIDInternal)context;
476
477   if (i->found == FALSE)
478     i->completion(i->client, i->conn, NULL, 0, i->context);
479
480   if (i->client_id)
481     silc_free(i->client_id);
482   silc_free(i);
483 }
484
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. */
488
489 void silc_client_get_client_by_id_resolve(SilcClient client,
490                                           SilcClientConnection conn,
491                                           SilcClientID *client_id,
492                                           SilcGetClientCallback completion,
493                                           void *context)
494 {
495   SilcBuffer idp;
496   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
497
498   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
499   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
500                            ++conn->cmd_ident,
501                            1, 3, idp->data, idp->len);
502   silc_buffer_free(idp);
503
504   i->client = client;
505   i->conn = conn;
506   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
507   i->completion = completion;
508   i->context = context;
509       
510   /* Add pending callback */
511   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
512                               conn->cmd_ident, 
513                               silc_client_get_client_by_id_destructor,
514                               silc_client_command_get_client_by_id_callback, 
515                               (void *)i);
516 }
517
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
520    to the channel. */
521
522 SilcChannelEntry silc_client_get_channel(SilcClient client,
523                                          SilcClientConnection conn,
524                                          char *channel)
525 {
526   SilcIDCacheEntry id_cache;
527   SilcChannelEntry entry;
528
529   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
530                                      &id_cache))
531     return NULL;
532
533   entry = (SilcChannelEntry)id_cache->context;
534
535   return entry;
536 }