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, 
128                                  &list))
129     return NULL;
130
131   if (!silc_idcache_list_count(list)) {
132     silc_idcache_list_free(list);
133     return NULL;
134   }
135
136   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
137   *clients_count = silc_idcache_list_count(list);
138
139   if (!server) {
140     /* Take all without any further checking */
141     silc_idcache_list_first(list, &id_cache);
142     while (id_cache) {
143       clients[i++] = id_cache->context;
144       if (!silc_idcache_list_next(list, &id_cache))
145         break;
146     }
147   } else {
148     /* Check multiple cache entries for match */
149     silc_idcache_list_first(list, &id_cache);
150     while (id_cache) {
151       entry = (SilcClientEntry)id_cache->context;
152       
153       if (entry->server && 
154           strncasecmp(server, entry->server, strlen(server))) {
155         if (!silc_idcache_list_next(list, &id_cache)) {
156           break;
157         } else {
158           continue;
159         }
160       }
161       
162       clients[i++] = id_cache->context;
163       if (!silc_idcache_list_next(list, &id_cache))
164         break;
165     }
166   }
167
168   if (list)
169     silc_idcache_list_free(list);
170
171   return clients;
172 }
173
174 typedef struct {
175   SilcClient client;
176   SilcClientConnection conn;
177   uint32 list_count;
178   SilcBuffer client_id_list;
179   SilcGetClientCallback completion;
180   void *context;
181   int found;
182 } *GetClientsByListInternal;
183
184 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
185 {
186   GetClientsByListInternal i = (GetClientsByListInternal)context;
187   SilcIDCacheEntry id_cache = NULL;
188   SilcBuffer client_id_list = i->client_id_list;
189   SilcClientEntry *clients = NULL;
190   uint32 clients_count = 0;
191   int c;
192
193   for (c = 0; c < i->list_count; c++) {
194     uint16 idp_len;
195     SilcClientID *client_id;
196
197     /* Get Client ID */
198     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
199     idp_len += 4;
200     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
201     if (!client_id)
202       continue;
203
204     /* Get the client entry */
205     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
206                                         (void *)client_id, 
207                                         NULL, NULL, 
208                                         silc_hash_client_id_compare, NULL,
209                                         &id_cache)) {
210       clients = silc_realloc(clients, sizeof(*clients) * 
211                              (clients_count + 1));
212       clients[clients_count] = (SilcClientEntry)id_cache->context;
213       clients_count++;
214       i->found = TRUE;
215     }
216
217     silc_free(client_id);
218     silc_buffer_pull(client_id_list, idp_len);
219   }
220
221   if (i->found) {
222     i->completion(i->client, i->conn, clients, clients_count, i->context);
223     silc_free(clients);
224   }
225 }
226
227 static void silc_client_get_clients_list_destructor(void *context)
228 {
229   GetClientsByListInternal i = (GetClientsByListInternal)context;
230
231   if (i->found == FALSE)
232     i->completion(i->client, i->conn, NULL, 0, i->context);
233
234   if (i->client_id_list)
235     silc_buffer_free(i->client_id_list);
236   silc_free(i);
237 }
238
239 /* Gets client entries by the list of client ID's `client_id_list'. This
240    always resolves those client ID's it does not know yet from the server
241    so this function might take a while. The `client_id_list' is a list
242    of ID Payloads added one after other.  JOIN command reply and USERS
243    command reply for example returns this sort of list. The `completion'
244    will be called after the entries are available. */
245
246 void silc_client_get_clients_by_list(SilcClient client,
247                                      SilcClientConnection conn,
248                                      uint32 list_count,
249                                      SilcBuffer client_id_list,
250                                      SilcGetClientCallback completion,
251                                      void *context)
252 {
253   SilcIDCacheEntry id_cache = NULL;
254   int i;
255   unsigned char **res_argv = NULL;
256   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
257   GetClientsByListInternal in;
258
259   in = silc_calloc(1, sizeof(*in));
260   in->client = client;
261   in->conn = conn;
262   in->list_count = list_count;
263   in->client_id_list = silc_buffer_copy(client_id_list);
264   in->completion = completion;
265   in->context = context;
266
267   for (i = 0; i < list_count; i++) {
268     uint16 idp_len;
269     SilcClientID *client_id;
270     SilcClientEntry entry;
271
272     /* Get Client ID */
273     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
274     idp_len += 4;
275     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
276     if (!client_id)
277       continue;
278
279     /* Check if we have this client cached already. */
280     id_cache = NULL;
281     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
282                                     NULL, NULL, 
283                                     silc_hash_client_id_compare, NULL,
284                                     &id_cache);
285
286     /* If we don't have the entry or it has incomplete info, then resolve
287        it from the server. */
288     entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
289     if (!id_cache || !entry->nickname) {
290       /* No we don't have it, query it from the server. Assemble argument
291          table that will be sent fr the IDENTIFY command later. */
292       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
293                               (res_argc + 1));
294       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
295                                    (res_argc + 1));
296       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
297                                     (res_argc + 1));
298       res_argv[res_argc] = client_id_list->data;
299       res_argv_lens[res_argc] = idp_len;
300       res_argv_types[res_argc] = res_argc + 3;
301       res_argc++;
302     }
303
304     silc_free(client_id);
305     silc_buffer_pull(client_id_list, idp_len);
306   }
307
308   /* Query the client information from server if the list included clients
309      that we don't know about. */
310   if (res_argc) {
311     SilcBuffer res_cmd;
312
313     /* Send the IDENTIFY command to server */
314     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
315                                           res_argc, res_argv, res_argv_lens,
316                                           res_argv_types, ++conn->cmd_ident);
317     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
318                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
319                             TRUE);
320
321     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
322                                 conn->cmd_ident, 
323                                 silc_client_get_clients_list_destructor,
324                                 silc_client_command_get_clients_list_callback, 
325                                 (void *)in);
326
327     silc_buffer_push(client_id_list, client_id_list->data - 
328                      client_id_list->head);
329     silc_buffer_free(res_cmd);
330     silc_free(res_argv);
331     silc_free(res_argv_lens);
332     silc_free(res_argv_types);
333     return;
334   }
335
336   silc_buffer_push(client_id_list, client_id_list->data - 
337                    client_id_list->head);
338
339   /* We have the clients in cache, get them and call the completion */
340   silc_client_command_get_clients_list_callback((void *)in);
341 }
342
343 /* The old style function to find client entry. This is used by the
344    library internally. If `query' is TRUE then the client information is
345    requested by the server. The pending command callback must be set
346    by the caller. */
347
348 SilcClientEntry silc_idlist_get_client(SilcClient client,
349                                        SilcClientConnection conn,
350                                        char *nickname,
351                                        char *server,
352                                        uint32 num,
353                                        int query)
354 {
355   SilcIDCacheEntry id_cache;
356   SilcIDCacheList list = NULL;
357   SilcClientEntry entry = NULL;
358
359   /* Find ID from cache */
360   if (!silc_idcache_find_by_name(conn->client_cache, nickname, 
361                                  &list)) {
362   identify:
363
364     if (query) {
365       char ident[512];
366       SilcClientCommandContext ctx;
367       
368       SILC_LOG_DEBUG(("Requesting Client ID from server"));
369       
370       /* No ID found. Do query from the server. The query is done by 
371          sending simple IDENTIFY command to the server. */
372       ctx = silc_client_command_alloc();
373       ctx->client = client;
374       ctx->conn = conn;
375       ctx->command = silc_client_command_find("IDENTIFY");
376       memset(ident, 0, sizeof(ident));
377       snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
378       silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
379                               &ctx->argv_types, &ctx->argc, 2);
380       ctx->command->cb(ctx);
381       
382       if (list)
383         silc_idcache_list_free(list);
384
385       return NULL;
386     }
387     return NULL;
388   }
389
390   if (!server && !num) {
391     /* Take first found cache entry */
392     if (!silc_idcache_list_first(list, &id_cache))
393       goto identify;
394
395     entry = (SilcClientEntry)id_cache->context;
396   } else {
397     /* Check multiple cache entries for match */
398     silc_idcache_list_first(list, &id_cache);
399     entry = (SilcClientEntry)id_cache->context;
400     
401     while (entry) {
402       if (server && entry->server && 
403           !strncasecmp(server, entry->server, strlen(server)))
404         break;
405       
406       if (num && entry->num == num)
407         break;
408
409       if (!silc_idcache_list_next(list, &id_cache)) {
410         entry = NULL;
411         break;
412       }
413
414       entry = (SilcClientEntry)id_cache->context;
415     }
416
417     /* If match weren't found, request it */
418     if (!entry)
419       goto identify;
420   }
421
422   if (list)
423     silc_idcache_list_free(list);
424
425   return entry;
426 }
427
428 /* Finds entry for client by the client's ID. Returns the entry or NULL
429    if the entry was not found. */
430
431 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
432                                              SilcClientConnection conn,
433                                              SilcClientID *client_id)
434 {
435   SilcIDCacheEntry id_cache;
436
437   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
438                   silc_id_render(client_id, SILC_ID_CLIENT)));
439
440   /* Find ID from cache */
441   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
442                                        NULL, NULL, 
443                                        silc_hash_client_id_compare, NULL,
444                                        &id_cache))
445     return NULL;
446
447   SILC_LOG_DEBUG(("Found"));
448
449   return (SilcClientEntry)id_cache->context;
450 }
451
452 typedef struct {
453   SilcClient client;
454   SilcClientConnection conn;
455   SilcClientID *client_id;
456   SilcGetClientCallback completion;
457   void *context;
458   int found;
459 } *GetClientByIDInternal;
460
461 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
462 {
463   GetClientByIDInternal i = (GetClientByIDInternal)context;
464   SilcClientEntry entry;
465
466   /* Get the client */
467   entry = silc_client_get_client_by_id(i->client, i->conn,
468                                        i->client_id);
469   if (entry) {
470     i->completion(i->client, i->conn, &entry, 1, i->context);
471     i->found = TRUE;
472   }
473 }
474
475 static void silc_client_get_client_by_id_destructor(void *context)
476 {
477   GetClientByIDInternal i = (GetClientByIDInternal)context;
478
479   if (i->found == FALSE)
480     i->completion(i->client, i->conn, NULL, 0, i->context);
481
482   if (i->client_id)
483     silc_free(i->client_id);
484   silc_free(i);
485 }
486
487 /* Same as above but will always resolve the information from the server.
488    Use this only if you know that you don't have the entry and the only
489    thing you know about the client is its ID. */
490
491 void silc_client_get_client_by_id_resolve(SilcClient client,
492                                           SilcClientConnection conn,
493                                           SilcClientID *client_id,
494                                           SilcGetClientCallback completion,
495                                           void *context)
496 {
497   SilcBuffer idp;
498   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
499
500   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
501   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
502                            ++conn->cmd_ident,
503                            1, 3, idp->data, idp->len);
504   silc_buffer_free(idp);
505
506   i->client = client;
507   i->conn = conn;
508   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
509   i->completion = completion;
510   i->context = context;
511       
512   /* Add pending callback */
513   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
514                               conn->cmd_ident, 
515                               silc_client_get_client_by_id_destructor,
516                               silc_client_command_get_client_by_id_callback, 
517                               (void *)i);
518 }
519
520 /* Finds entry for channel by the channel name. Returns the entry or NULL
521    if the entry was not found. It is found only if the client is joined
522    to the channel. */
523
524 SilcChannelEntry silc_client_get_channel(SilcClient client,
525                                          SilcClientConnection conn,
526                                          char *channel)
527 {
528   SilcIDCacheEntry id_cache;
529   SilcChannelEntry entry;
530
531   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
532                                      &id_cache))
533     return NULL;
534
535   entry = (SilcChannelEntry)id_cache->context;
536
537   return entry;
538 }