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 /* The old style function to find client entry. This is used by the
174    library internally. If `query' is TRUE then the client information is
175    requested by the server. The pending command callback must be set
176    by the caller. */
177
178 SilcClientEntry silc_idlist_get_client(SilcClient client,
179                                        SilcClientConnection conn,
180                                        char *nickname,
181                                        char *server,
182                                        unsigned int num,
183                                        int query)
184 {
185   SilcIDCacheEntry id_cache;
186   SilcIDCacheList list = NULL;
187   SilcClientEntry entry = NULL;
188
189   /* Find ID from cache */
190   if (!silc_idcache_find_by_data_loose(conn->client_cache, nickname, &list)) {
191   identify:
192
193     if (query) {
194       char ident[512];
195       SilcClientCommandContext ctx;
196       
197       SILC_LOG_DEBUG(("Requesting Client ID from server"));
198       
199       /* No ID found. Do query from the server. The query is done by 
200          sending simple IDENTIFY command to the server. */
201       ctx = silc_client_command_alloc();
202       ctx->client = client;
203       ctx->conn = conn;
204       ctx->command = silc_client_command_find("IDENTIFY");
205       memset(ident, 0, sizeof(ident));
206       snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
207       silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
208                               &ctx->argv_types, &ctx->argc, 2);
209       ctx->command->cb(ctx);
210       
211       if (list)
212         silc_idcache_list_free(list);
213
214       return NULL;
215     }
216     return NULL;
217   }
218
219   if (!server && !num) {
220     /* Take first found cache entry */
221     if (!silc_idcache_list_first(list, &id_cache))
222       goto identify;
223
224     entry = (SilcClientEntry)id_cache->context;
225   } else {
226     /* Check multiple cache entries for match */
227     silc_idcache_list_first(list, &id_cache);
228     entry = (SilcClientEntry)id_cache->context;
229     
230     while (entry) {
231       if (server && entry->server && 
232           !strncasecmp(server, entry->server, strlen(server)))
233         break;
234       
235       if (num && entry->num == num)
236         break;
237
238       if (!silc_idcache_list_next(list, &id_cache)) {
239         entry = NULL;
240         break;
241       }
242
243       entry = (SilcClientEntry)id_cache->context;
244     }
245
246     /* If match weren't found, request it */
247     if (!entry)
248       goto identify;
249   }
250
251   if (list)
252     silc_idcache_list_free(list);
253
254   return entry;
255 }
256
257 /* Finds entry for client by the client's ID. Returns the entry or NULL
258    if the entry was not found. */
259
260 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
261                                              SilcClientConnection conn,
262                                              SilcClientID *client_id)
263 {
264   SilcIDCacheEntry id_cache;
265
266   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
267                   silc_id_render(client_id, SILC_ID_CLIENT)));
268
269   /* Find ID from cache */
270   if (!silc_idcache_find_by_id_one(conn->client_cache, client_id, 
271                                    SILC_ID_CLIENT, &id_cache))
272     return NULL;
273
274   SILC_LOG_DEBUG(("Found"));
275
276   return (SilcClientEntry)id_cache->context;
277 }
278
279 typedef struct {
280   SilcClient client;
281   SilcClientConnection conn;
282   SilcClientID *client_id;
283   SilcGetClientCallback completion;
284   void *context;
285   int found;
286 } *GetClientByIDInternal;
287
288 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
289 {
290   GetClientByIDInternal i = (GetClientByIDInternal)context;
291   SilcClientEntry entry;
292
293   /* Get the client */
294   entry = silc_client_get_client_by_id(i->client, i->conn,
295                                        i->client_id);
296   if (entry) {
297     i->completion(i->client, i->conn, &entry, 1, i->context);
298     i->found = TRUE;
299   }
300 }
301
302 static void silc_client_get_client_by_id_destructor(void *context)
303 {
304   GetClientByIDInternal i = (GetClientByIDInternal)context;
305
306   if (i->found == FALSE)
307     i->completion(i->client, i->conn, NULL, 0, i->context);
308
309   if (i->client_id)
310     silc_free(i->client_id);
311   silc_free(i);
312 }
313
314 /* Same as above but will always resolve the information from the server.
315    Use this only if you know that you don't have the entry and the only
316    thing you know about the client is its ID. */
317
318 void silc_client_get_client_by_id_resolve(SilcClient client,
319                                           SilcClientConnection conn,
320                                           SilcClientID *client_id,
321                                           SilcGetClientCallback completion,
322                                           void *context)
323 {
324   SilcBuffer idp;
325   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
326
327   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
328   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
329                            ++conn->cmd_ident,
330                            1, 3, idp->data, idp->len);
331   silc_buffer_free(idp);
332
333   i->client = client;
334   i->conn = conn;
335   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
336   i->completion = completion;
337   i->context = context;
338       
339   /* Add pending callback */
340   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
341                               ++conn->cmd_ident, 
342                               silc_client_get_client_by_id_destructor,
343                               silc_client_command_get_client_by_id_callback, 
344                               (void *)i);
345 }
346
347 /* Finds entry for channel by the channel name. Returns the entry or NULL
348    if the entry was not found. It is found only if the client is joined
349    to the channel. */
350
351 SilcChannelEntry silc_client_get_channel(SilcClient client,
352                                          SilcClientConnection conn,
353                                          char *channel)
354 {
355   SilcIDCacheEntry id_cache;
356   SilcChannelEntry entry;
357
358   if (!silc_idcache_find_by_data_one(conn->channel_cache, channel, &id_cache))
359     return NULL;
360
361   entry = (SilcChannelEntry)id_cache->context;
362
363   return entry;
364 }