updates. New data types.
[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_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   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(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                                      uint32 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   uint32 *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     uint16 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
302   /* Query the client information from server if the list included clients
303      that we don't know about. */
304   if (res_argc) {
305     SilcBuffer res_cmd;
306
307     /* Send the IDENTIFY command to server */
308     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
309                                           res_argc, res_argv, res_argv_lens,
310                                           res_argv_types, ++conn->cmd_ident);
311     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
312                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
313                             TRUE);
314
315     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
316                                 conn->cmd_ident, 
317                                 silc_client_get_clients_list_destructor,
318                                 silc_client_command_get_clients_list_callback, 
319                                 (void *)in);
320
321     silc_buffer_push(client_id_list, client_id_list->data - 
322                      client_id_list->head);
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   silc_buffer_push(client_id_list, client_id_list->data - 
331                    client_id_list->head);
332
333   /* We have the clients in cache, get them and call the completion */
334   silc_client_command_get_clients_list_callback((void *)in);
335 }
336
337 /* The old style function to find client entry. This is used by the
338    library internally. If `query' is TRUE then the client information is
339    requested by the server. The pending command callback must be set
340    by the caller. */
341
342 SilcClientEntry silc_idlist_get_client(SilcClient client,
343                                        SilcClientConnection conn,
344                                        char *nickname,
345                                        char *server,
346                                        uint32 num,
347                                        int query)
348 {
349   SilcIDCacheEntry id_cache;
350   SilcIDCacheList list = NULL;
351   SilcClientEntry entry = NULL;
352
353   /* Find ID from cache */
354   if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list)) {
355   identify:
356
357     if (query) {
358       char ident[512];
359       SilcClientCommandContext ctx;
360       
361       SILC_LOG_DEBUG(("Requesting Client ID from server"));
362       
363       /* No ID found. Do query from the server. The query is done by 
364          sending simple IDENTIFY command to the server. */
365       ctx = silc_client_command_alloc();
366       ctx->client = client;
367       ctx->conn = conn;
368       ctx->command = silc_client_command_find("IDENTIFY");
369       memset(ident, 0, sizeof(ident));
370       snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
371       silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
372                               &ctx->argv_types, &ctx->argc, 2);
373       ctx->command->cb(ctx);
374       
375       if (list)
376         silc_idcache_list_free(list);
377
378       return NULL;
379     }
380     return NULL;
381   }
382
383   if (!server && !num) {
384     /* Take first found cache entry */
385     if (!silc_idcache_list_first(list, &id_cache))
386       goto identify;
387
388     entry = (SilcClientEntry)id_cache->context;
389   } else {
390     /* Check multiple cache entries for match */
391     silc_idcache_list_first(list, &id_cache);
392     entry = (SilcClientEntry)id_cache->context;
393     
394     while (entry) {
395       if (server && entry->server && 
396           !strncasecmp(server, entry->server, strlen(server)))
397         break;
398       
399       if (num && entry->num == num)
400         break;
401
402       if (!silc_idcache_list_next(list, &id_cache)) {
403         entry = NULL;
404         break;
405       }
406
407       entry = (SilcClientEntry)id_cache->context;
408     }
409
410     /* If match weren't found, request it */
411     if (!entry)
412       goto identify;
413   }
414
415   if (list)
416     silc_idcache_list_free(list);
417
418   return entry;
419 }
420
421 /* Finds entry for client by the client's ID. Returns the entry or NULL
422    if the entry was not found. */
423
424 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
425                                              SilcClientConnection conn,
426                                              SilcClientID *client_id)
427 {
428   SilcIDCacheEntry id_cache;
429
430   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
431                   silc_id_render(client_id, SILC_ID_CLIENT)));
432
433   /* Find ID from cache */
434   if (!silc_idcache_find_by_id_one(conn->client_cache, client_id, 
435                                    SILC_ID_CLIENT, &id_cache))
436     return NULL;
437
438   SILC_LOG_DEBUG(("Found"));
439
440   return (SilcClientEntry)id_cache->context;
441 }
442
443 typedef struct {
444   SilcClient client;
445   SilcClientConnection conn;
446   SilcClientID *client_id;
447   SilcGetClientCallback completion;
448   void *context;
449   int found;
450 } *GetClientByIDInternal;
451
452 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
453 {
454   GetClientByIDInternal i = (GetClientByIDInternal)context;
455   SilcClientEntry entry;
456
457   /* Get the client */
458   entry = silc_client_get_client_by_id(i->client, i->conn,
459                                        i->client_id);
460   if (entry) {
461     i->completion(i->client, i->conn, &entry, 1, i->context);
462     i->found = TRUE;
463   }
464 }
465
466 static void silc_client_get_client_by_id_destructor(void *context)
467 {
468   GetClientByIDInternal i = (GetClientByIDInternal)context;
469
470   if (i->found == FALSE)
471     i->completion(i->client, i->conn, NULL, 0, i->context);
472
473   if (i->client_id)
474     silc_free(i->client_id);
475   silc_free(i);
476 }
477
478 /* Same as above but will always resolve the information from the server.
479    Use this only if you know that you don't have the entry and the only
480    thing you know about the client is its ID. */
481
482 void silc_client_get_client_by_id_resolve(SilcClient client,
483                                           SilcClientConnection conn,
484                                           SilcClientID *client_id,
485                                           SilcGetClientCallback completion,
486                                           void *context)
487 {
488   SilcBuffer idp;
489   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
490
491   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
492   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
493                            ++conn->cmd_ident,
494                            1, 3, idp->data, idp->len);
495   silc_buffer_free(idp);
496
497   i->client = client;
498   i->conn = conn;
499   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
500   i->completion = completion;
501   i->context = context;
502       
503   /* Add pending callback */
504   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
505                               conn->cmd_ident, 
506                               silc_client_get_client_by_id_destructor,
507                               silc_client_command_get_client_by_id_callback, 
508                               (void *)i);
509 }
510
511 /* Finds entry for channel by the channel name. Returns the entry or NULL
512    if the entry was not found. It is found only if the client is joined
513    to the channel. */
514
515 SilcChannelEntry silc_client_get_channel(SilcClient client,
516                                          SilcClientConnection conn,
517                                          char *channel)
518 {
519   SilcIDCacheEntry id_cache;
520   SilcChannelEntry entry;
521
522   if (!silc_idcache_find_by_data_one(conn->channel_cache, channel, &id_cache))
523     return NULL;
524
525   entry = (SilcChannelEntry)id_cache->context;
526
527   return entry;
528 }