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   unsigned int 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                                                unsigned int *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_data_loose(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   unsigned int 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   unsigned int clients_count = 0;
190   int c;
191
192   for (c = 0; c < i->list_count; c++) {
193     unsigned short 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(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;
209       clients_count++;
210       i->found = TRUE;
211     }
212
213     silc_free(client_id);
214     silc_buffer_pull(client_id_list, idp_len);
215   }
216
217   if (i->found) {
218     i->completion(i->client, i->conn, clients, clients_count, i->context);
219     silc_free(clients);
220   }
221 }
222
223 static void silc_client_get_clients_list_destructor(void *context)
224 {
225   GetClientsByListInternal i = (GetClientsByListInternal)context;
226
227   if (i->found == FALSE)
228     i->completion(i->client, i->conn, NULL, 0, i->context);
229
230   if (i->client_id_list)
231     silc_buffer_free(i->client_id_list);
232   silc_free(i);
233 }
234
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. */
241
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,
247                                      void *context)
248 {
249   SilcIDCacheEntry id_cache = NULL;
250   int i;
251   unsigned char **res_argv = NULL;
252   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
253   GetClientsByListInternal in;
254
255   in = silc_calloc(1, sizeof(*in));
256   in->client = client;
257   in->conn = conn;
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;
262
263   for (i = 0; i < list_count; i++) {
264     unsigned short idp_len;
265     SilcClientID *client_id;
266     SilcClientEntry entry;
267
268     /* Get Client ID */
269     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
270     idp_len += 4;
271     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
272     if (!client_id)
273       continue;
274
275     /* Check if we have this client cached already. */
276     id_cache = NULL;
277     silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
278                                 SILC_ID_CLIENT, &id_cache);
279
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) *
287                               (res_argc + 1));
288       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
289                                    (res_argc + 1));
290       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
291                                     (res_argc + 1));
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;
295       res_argc++;
296     }
297
298     silc_free(client_id);
299     silc_buffer_pull(client_id_list, idp_len);
300   }
301   silc_buffer_push(client_id_list, client_id_list->data - 
302                    client_id_list->head);
303
304   /* Query the client information from server if the list included clients
305      that we don't know about. */
306   if (res_argc) {
307     SilcBuffer res_cmd;
308
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,
315                             TRUE);
316
317     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
318                                 conn->cmd_ident, 
319                                 silc_client_get_clients_list_destructor,
320                                 silc_client_command_get_clients_list_callback, 
321                                 (void *)in);
322
323     silc_buffer_free(res_cmd);
324     silc_free(res_argv);
325     silc_free(res_argv_lens);
326     silc_free(res_argv_types);
327     return;
328   }
329
330   /* We have the clients in cache, get them and call the completion */
331   silc_client_command_get_clients_list_callback((void *)in);
332 }
333
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
337    by the caller. */
338
339 SilcClientEntry silc_idlist_get_client(SilcClient client,
340                                        SilcClientConnection conn,
341                                        char *nickname,
342                                        char *server,
343                                        unsigned int num,
344                                        int query)
345 {
346   SilcIDCacheEntry id_cache;
347   SilcIDCacheList list = NULL;
348   SilcClientEntry entry = NULL;
349
350   /* Find ID from cache */
351   if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list)) {
352   identify:
353
354     if (query) {
355       char ident[512];
356       SilcClientCommandContext ctx;
357       
358       SILC_LOG_DEBUG(("Requesting Client ID from server"));
359       
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;
364       ctx->conn = conn;
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);
371       
372       if (list)
373         silc_idcache_list_free(list);
374
375       return NULL;
376     }
377     return NULL;
378   }
379
380   if (!server && !num) {
381     /* Take first found cache entry */
382     if (!silc_idcache_list_first(list, &id_cache))
383       goto identify;
384
385     entry = (SilcClientEntry)id_cache->context;
386   } else {
387     /* Check multiple cache entries for match */
388     silc_idcache_list_first(list, &id_cache);
389     entry = (SilcClientEntry)id_cache->context;
390     
391     while (entry) {
392       if (server && entry->server && 
393           !strncasecmp(server, entry->server, strlen(server)))
394         break;
395       
396       if (num && entry->num == num)
397         break;
398
399       if (!silc_idcache_list_next(list, &id_cache)) {
400         entry = NULL;
401         break;
402       }
403
404       entry = (SilcClientEntry)id_cache->context;
405     }
406
407     /* If match weren't found, request it */
408     if (!entry)
409       goto identify;
410   }
411
412   if (list)
413     silc_idcache_list_free(list);
414
415   return entry;
416 }
417
418 /* Finds entry for client by the client's ID. Returns the entry or NULL
419    if the entry was not found. */
420
421 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
422                                              SilcClientConnection conn,
423                                              SilcClientID *client_id)
424 {
425   SilcIDCacheEntry id_cache;
426
427   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
428                   silc_id_render(client_id, SILC_ID_CLIENT)));
429
430   /* Find ID from cache */
431   if (!silc_idcache_find_by_id_one(conn->client_cache, client_id, 
432                                    SILC_ID_CLIENT, &id_cache))
433     return NULL;
434
435   SILC_LOG_DEBUG(("Found"));
436
437   return (SilcClientEntry)id_cache->context;
438 }
439
440 typedef struct {
441   SilcClient client;
442   SilcClientConnection conn;
443   SilcClientID *client_id;
444   SilcGetClientCallback completion;
445   void *context;
446   int found;
447 } *GetClientByIDInternal;
448
449 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
450 {
451   GetClientByIDInternal i = (GetClientByIDInternal)context;
452   SilcClientEntry entry;
453
454   /* Get the client */
455   entry = silc_client_get_client_by_id(i->client, i->conn,
456                                        i->client_id);
457   if (entry) {
458     i->completion(i->client, i->conn, &entry, 1, i->context);
459     i->found = TRUE;
460   }
461 }
462
463 static void silc_client_get_client_by_id_destructor(void *context)
464 {
465   GetClientByIDInternal i = (GetClientByIDInternal)context;
466
467   if (i->found == FALSE)
468     i->completion(i->client, i->conn, NULL, 0, i->context);
469
470   if (i->client_id)
471     silc_free(i->client_id);
472   silc_free(i);
473 }
474
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. */
478
479 void silc_client_get_client_by_id_resolve(SilcClient client,
480                                           SilcClientConnection conn,
481                                           SilcClientID *client_id,
482                                           SilcGetClientCallback completion,
483                                           void *context)
484 {
485   SilcBuffer idp;
486   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
487
488   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
489   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
490                            ++conn->cmd_ident,
491                            1, 3, idp->data, idp->len);
492   silc_buffer_free(idp);
493
494   i->client = client;
495   i->conn = conn;
496   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
497   i->completion = completion;
498   i->context = context;
499       
500   /* Add pending callback */
501   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
502                               conn->cmd_ident, 
503                               silc_client_get_client_by_id_destructor,
504                               silc_client_command_get_client_by_id_callback, 
505                               (void *)i);
506 }
507
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
510    to the channel. */
511
512 SilcChannelEntry silc_client_get_channel(SilcClient client,
513                                          SilcClientConnection conn,
514                                          char *channel)
515 {
516   SilcIDCacheEntry id_cache;
517   SilcChannelEntry entry;
518
519   if (!silc_idcache_find_by_data_one(conn->channel_cache, channel, &id_cache))
520     return NULL;
521
522   entry = (SilcChannelEntry)id_cache->context;
523
524   return entry;
525 }