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
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;
102
103   /* Call the command */
104   ctx->command->cb(ctx);
105
106   /* Add pending callback */
107   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
108                               conn->cmd_ident, 
109                               silc_client_get_client_destructor,
110                               silc_client_command_get_client_callback, 
111                               (void *)i);
112 }
113
114 /* Same as above function but does not resolve anything from the server.
115    This checks local cache and returns all clients from the cache. */
116
117 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
118                                                SilcClientConnection conn,
119                                                char *nickname,
120                                                char *server,
121                                                uint32 *clients_count)
122 {
123   SilcIDCacheEntry id_cache;
124   SilcIDCacheList list = NULL;
125   SilcClientEntry entry, *clients;
126   int i = 0;
127
128   /* Find ID from cache */
129   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
130     return NULL;
131
132   if (!silc_idcache_list_count(list)) {
133     silc_idcache_list_free(list);
134     return NULL;
135   }
136
137   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
138   *clients_count = silc_idcache_list_count(list);
139
140   if (!server) {
141     /* Take all without any further checking */
142     silc_idcache_list_first(list, &id_cache);
143     while (id_cache) {
144       clients[i++] = id_cache->context;
145       if (!silc_idcache_list_next(list, &id_cache))
146         break;
147     }
148   } else {
149     /* Check multiple cache entries for match */
150     silc_idcache_list_first(list, &id_cache);
151     while (id_cache) {
152       entry = (SilcClientEntry)id_cache->context;
153       
154       if (entry->server && 
155           strncasecmp(server, entry->server, strlen(server))) {
156         if (!silc_idcache_list_next(list, &id_cache)) {
157           break;
158         } else {
159           continue;
160         }
161       }
162       
163       clients[i++] = id_cache->context;
164       if (!silc_idcache_list_next(list, &id_cache))
165         break;
166     }
167   }
168
169   if (list)
170     silc_idcache_list_free(list);
171
172   return clients;
173 }
174
175 typedef struct {
176   SilcClient client;
177   SilcClientConnection conn;
178   uint32 list_count;
179   SilcBuffer client_id_list;
180   SilcGetClientCallback completion;
181   void *context;
182   int found;
183 } *GetClientsByListInternal;
184
185 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
186 {
187   GetClientsByListInternal i = (GetClientsByListInternal)context;
188   SilcIDCacheEntry id_cache = NULL;
189   SilcBuffer client_id_list = i->client_id_list;
190   SilcClientEntry *clients = NULL;
191   uint32 clients_count = 0;
192   int c;
193
194   for (c = 0; c < i->list_count; c++) {
195     uint16 idp_len;
196     SilcClientID *client_id;
197
198     /* Get Client ID */
199     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
200     idp_len += 4;
201     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
202     if (!client_id)
203       continue;
204
205     /* Get the client entry */
206     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
207                                         (void *)client_id, 
208                                         NULL, NULL, 
209                                         silc_hash_client_id_compare, NULL,
210                                         &id_cache)) {
211       clients = silc_realloc(clients, sizeof(*clients) * 
212                              (clients_count + 1));
213       clients[clients_count] = (SilcClientEntry)id_cache->context;
214       clients_count++;
215       i->found = TRUE;
216     }
217
218     silc_free(client_id);
219     silc_buffer_pull(client_id_list, idp_len);
220   }
221
222   if (i->found) {
223     i->completion(i->client, i->conn, clients, clients_count, i->context);
224     silc_free(clients);
225   }
226 }
227
228 static void silc_client_get_clients_list_destructor(void *context)
229 {
230   GetClientsByListInternal i = (GetClientsByListInternal)context;
231
232   if (i->found == FALSE)
233     i->completion(i->client, i->conn, NULL, 0, i->context);
234
235   if (i->client_id_list)
236     silc_buffer_free(i->client_id_list);
237   silc_free(i);
238 }
239
240 /* Gets client entries by the list of client ID's `client_id_list'. This
241    always resolves those client ID's it does not know yet from the server
242    so this function might take a while. The `client_id_list' is a list
243    of ID Payloads added one after other.  JOIN command reply and USERS
244    command reply for example returns this sort of list. The `completion'
245    will be called after the entries are available. */
246
247 void silc_client_get_clients_by_list(SilcClient client,
248                                      SilcClientConnection conn,
249                                      uint32 list_count,
250                                      SilcBuffer client_id_list,
251                                      SilcGetClientCallback completion,
252                                      void *context)
253 {
254   SilcIDCacheEntry id_cache = NULL;
255   int i;
256   unsigned char **res_argv = NULL;
257   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
258   GetClientsByListInternal in;
259
260   in = silc_calloc(1, sizeof(*in));
261   in->client = client;
262   in->conn = conn;
263   in->list_count = list_count;
264   in->client_id_list = silc_buffer_copy(client_id_list);
265   in->completion = completion;
266   in->context = context;
267
268   for (i = 0; i < list_count; i++) {
269     uint16 idp_len;
270     SilcClientID *client_id;
271     SilcClientEntry entry;
272
273     /* Get Client ID */
274     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
275     idp_len += 4;
276     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
277     if (!client_id)
278       continue;
279
280     /* Check if we have this client cached already. */
281     id_cache = NULL;
282     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
283                                     NULL, NULL, 
284                                     silc_hash_client_id_compare, NULL,
285                                     &id_cache);
286
287     /* If we don't have the entry or it has incomplete info, then resolve
288        it from the server. */
289     entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
290     if (!id_cache || !entry->nickname) {
291       /* No we don't have it, query it from the server. Assemble argument
292          table that will be sent fr the IDENTIFY command later. */
293       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
294                               (res_argc + 1));
295       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
296                                    (res_argc + 1));
297       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
298                                     (res_argc + 1));
299       res_argv[res_argc] = client_id_list->data;
300       res_argv_lens[res_argc] = idp_len;
301       res_argv_types[res_argc] = res_argc + 3;
302       res_argc++;
303     }
304
305     silc_free(client_id);
306     silc_buffer_pull(client_id_list, idp_len);
307   }
308
309   /* Query the client information from server if the list included clients
310      that we don't know about. */
311   if (res_argc) {
312     SilcBuffer res_cmd;
313
314     /* Send the IDENTIFY command to server */
315     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
316                                           res_argc, res_argv, res_argv_lens,
317                                           res_argv_types, ++conn->cmd_ident);
318     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
319                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
320                             TRUE);
321
322     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
323                                 conn->cmd_ident, 
324                                 silc_client_get_clients_list_destructor,
325                                 silc_client_command_get_clients_list_callback, 
326                                 (void *)in);
327
328     silc_buffer_push(client_id_list, client_id_list->data - 
329                      client_id_list->head);
330     silc_buffer_free(res_cmd);
331     silc_free(res_argv);
332     silc_free(res_argv_lens);
333     silc_free(res_argv_types);
334     return;
335   }
336
337   silc_buffer_push(client_id_list, client_id_list->data - 
338                    client_id_list->head);
339
340   /* We have the clients in cache, get them and call the completion */
341   silc_client_command_get_clients_list_callback((void *)in);
342 }
343
344 /* The old style function to find client entry. This is used by the
345    library internally. If `query' is TRUE then the client information is
346    requested by the server. The pending command callback must be set
347    by the caller. */
348
349 SilcClientEntry silc_idlist_get_client(SilcClient client,
350                                        SilcClientConnection conn,
351                                        char *nickname,
352                                        char *server,
353                                        uint32 num,
354                                        int query)
355 {
356   SilcIDCacheEntry id_cache;
357   SilcIDCacheList list = NULL;
358   SilcClientEntry entry = NULL;
359
360   /* Find ID from cache */
361   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &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 /* Removes client from the cache by the client entry. */
521
522 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
523                             SilcClientEntry client_entry)
524 {
525   return silc_idcache_del_by_context(conn->client_cache, client_entry);
526 }
527
528 /* Removes client from the cache by the client ID. */
529
530 bool silc_client_del_client_by_id(SilcClient client, 
531                                   SilcClientConnection conn,
532                                   SilcClientID *client_id)
533 {
534   return silc_idcache_del_by_id_ext(conn->client_cache, (void *)client_id, 
535                                     NULL, NULL, 
536                                     silc_hash_client_id_compare, NULL);
537 }
538
539 /* Finds entry for channel by the channel name. Returns the entry or NULL
540    if the entry was not found. It is found only if the client is joined
541    to the channel. */
542
543 SilcChannelEntry silc_client_get_channel(SilcClient client,
544                                          SilcClientConnection conn,
545                                          char *channel)
546 {
547   SilcIDCacheEntry id_cache;
548   SilcChannelEntry entry;
549
550   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
551                                      &id_cache))
552     return NULL;
553
554   entry = (SilcChannelEntry)id_cache->context;
555
556   return entry;
557 }