66270cdc3010f5efed85d399551a16cd1b02bbeb
[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, NULL);
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   bool found = FALSE;
128
129   /* Find ID from cache */
130   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list))
131     return NULL;
132
133   if (!silc_idcache_list_count(list)) {
134     silc_idcache_list_free(list);
135     return NULL;
136   }
137
138   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
139   *clients_count = silc_idcache_list_count(list);
140
141   if (!server) {
142     /* Take all without any further checking */
143     silc_idcache_list_first(list, &id_cache);
144     while (id_cache) {
145       clients[i++] = id_cache->context;
146       found = TRUE;
147       if (!silc_idcache_list_next(list, &id_cache))
148         break;
149     }
150   } else {
151     /* Check multiple cache entries for match */
152     silc_idcache_list_first(list, &id_cache);
153     while (id_cache) {
154       entry = (SilcClientEntry)id_cache->context;
155       
156       if (entry->server && 
157           strncasecmp(server, entry->server, strlen(server))) {
158         if (!silc_idcache_list_next(list, &id_cache)) {
159           break;
160         } else {
161           continue;
162         }
163       }
164       
165       clients[i++] = id_cache->context;
166       found = TRUE;
167       if (!silc_idcache_list_next(list, &id_cache))
168         break;
169     }
170   }
171
172   if (list)
173     silc_idcache_list_free(list);
174
175   if (!found) {
176     *clients_count = 0;
177     if (clients)
178       silc_free(clients);
179     return NULL;
180   }
181
182   return clients;
183 }
184
185 typedef struct {
186   SilcClient client;
187   SilcClientConnection conn;
188   uint32 list_count;
189   SilcBuffer client_id_list;
190   SilcGetClientCallback completion;
191   void *context;
192   int found;
193 } *GetClientsByListInternal;
194
195 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
196 {
197   GetClientsByListInternal i = (GetClientsByListInternal)context;
198   SilcIDCacheEntry id_cache = NULL;
199   SilcBuffer client_id_list = i->client_id_list;
200   SilcClientEntry *clients = NULL;
201   uint32 clients_count = 0;
202   int c;
203
204   for (c = 0; c < i->list_count; c++) {
205     uint16 idp_len;
206     SilcClientID *client_id;
207
208     /* Get Client ID */
209     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
210     idp_len += 4;
211     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
212     if (!client_id)
213       continue;
214
215     /* Get the client entry */
216     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
217                                         (void *)client_id, 
218                                         NULL, NULL, 
219                                         silc_hash_client_id_compare, NULL,
220                                         &id_cache)) {
221       clients = silc_realloc(clients, sizeof(*clients) * 
222                              (clients_count + 1));
223       clients[clients_count] = (SilcClientEntry)id_cache->context;
224       clients_count++;
225       i->found = TRUE;
226     }
227
228     silc_free(client_id);
229     silc_buffer_pull(client_id_list, idp_len);
230   }
231
232   if (i->found) {
233     i->completion(i->client, i->conn, clients, clients_count, i->context);
234     silc_free(clients);
235   }
236 }
237
238 static void silc_client_get_clients_list_destructor(void *context)
239 {
240   GetClientsByListInternal i = (GetClientsByListInternal)context;
241
242   if (i->found == FALSE)
243     i->completion(i->client, i->conn, NULL, 0, i->context);
244
245   if (i->client_id_list)
246     silc_buffer_free(i->client_id_list);
247   silc_free(i);
248 }
249
250 /* Gets client entries by the list of client ID's `client_id_list'. This
251    always resolves those client ID's it does not know yet from the server
252    so this function might take a while. The `client_id_list' is a list
253    of ID Payloads added one after other.  JOIN command reply and USERS
254    command reply for example returns this sort of list. The `completion'
255    will be called after the entries are available. */
256
257 void silc_client_get_clients_by_list(SilcClient client,
258                                      SilcClientConnection conn,
259                                      uint32 list_count,
260                                      SilcBuffer client_id_list,
261                                      SilcGetClientCallback completion,
262                                      void *context)
263 {
264   SilcIDCacheEntry id_cache = NULL;
265   int i;
266   unsigned char **res_argv = NULL;
267   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
268   GetClientsByListInternal in;
269
270   in = silc_calloc(1, sizeof(*in));
271   in->client = client;
272   in->conn = conn;
273   in->list_count = list_count;
274   in->client_id_list = silc_buffer_copy(client_id_list);
275   in->completion = completion;
276   in->context = context;
277
278   for (i = 0; i < list_count; i++) {
279     uint16 idp_len;
280     SilcClientID *client_id;
281     SilcClientEntry entry;
282
283     /* Get Client ID */
284     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
285     idp_len += 4;
286     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
287     if (!client_id)
288       continue;
289
290     /* Check if we have this client cached already. */
291     id_cache = NULL;
292     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
293                                     NULL, NULL, 
294                                     silc_hash_client_id_compare, NULL,
295                                     &id_cache);
296
297     /* If we don't have the entry or it has incomplete info, then resolve
298        it from the server. */
299     entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
300     if (!id_cache || !entry->nickname) {
301       /* No we don't have it, query it from the server. Assemble argument
302          table that will be sent fr the IDENTIFY command later. */
303       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
304                               (res_argc + 1));
305       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
306                                    (res_argc + 1));
307       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
308                                     (res_argc + 1));
309       res_argv[res_argc] = client_id_list->data;
310       res_argv_lens[res_argc] = idp_len;
311       res_argv_types[res_argc] = res_argc + 5;
312       res_argc++;
313     }
314
315     silc_free(client_id);
316     silc_buffer_pull(client_id_list, idp_len);
317   }
318
319   /* Query the client information from server if the list included clients
320      that we don't know about. */
321   if (res_argc) {
322     SilcBuffer res_cmd;
323
324     /* Send the IDENTIFY command to server */
325     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
326                                           res_argc, res_argv, res_argv_lens,
327                                           res_argv_types, ++conn->cmd_ident);
328     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
329                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
330                             TRUE);
331
332     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
333                                 conn->cmd_ident, 
334                                 silc_client_get_clients_list_destructor,
335                                 silc_client_command_get_clients_list_callback, 
336                                 (void *)in);
337
338     silc_buffer_push(client_id_list, client_id_list->data - 
339                      client_id_list->head);
340     silc_buffer_free(res_cmd);
341     silc_free(res_argv);
342     silc_free(res_argv_lens);
343     silc_free(res_argv_types);
344     return;
345   }
346
347   silc_buffer_push(client_id_list, client_id_list->data - 
348                    client_id_list->head);
349
350   /* We have the clients in cache, get them and call the completion */
351   silc_client_command_get_clients_list_callback((void *)in, NULL);
352 }
353
354 /* The old style function to find client entry. This is used by the
355    library internally. If `query' is TRUE then the client information is
356    requested by the server. The pending command callback must be set
357    by the caller. */
358
359 SilcClientEntry silc_idlist_get_client(SilcClient client,
360                                        SilcClientConnection conn,
361                                        char *nickname,
362                                        char *server,
363                                        uint32 num,
364                                        int query)
365 {
366   SilcIDCacheEntry id_cache;
367   SilcIDCacheList list = NULL;
368   SilcClientEntry entry = NULL;
369
370   /* Find ID from cache */
371   if (!silc_idcache_find_by_name(conn->client_cache, nickname, &list)) {
372   identify:
373
374     if (query) {
375       char ident[512];
376       SilcClientCommandContext ctx;
377       
378       SILC_LOG_DEBUG(("Requesting Client ID from server"));
379       
380       /* No ID found. Do query from the server. The query is done by 
381          sending simple IDENTIFY command to the server. */
382       ctx = silc_client_command_alloc();
383       ctx->client = client;
384       ctx->conn = conn;
385       ctx->command = silc_client_command_find("IDENTIFY");
386       memset(ident, 0, sizeof(ident));
387       snprintf(ident, sizeof(ident), "IDENTIFY %s", nickname);
388       silc_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
389                               &ctx->argv_types, &ctx->argc, 2);
390       ctx->command->cb(ctx, NULL);
391       
392       if (list)
393         silc_idcache_list_free(list);
394
395       return NULL;
396     }
397     return NULL;
398   }
399
400   if (!server && !num) {
401     /* Take first found cache entry */
402     if (!silc_idcache_list_first(list, &id_cache))
403       goto identify;
404
405     entry = (SilcClientEntry)id_cache->context;
406   } else {
407     /* Check multiple cache entries for match */
408     silc_idcache_list_first(list, &id_cache);
409     entry = (SilcClientEntry)id_cache->context;
410     
411     while (entry) {
412       if (server && entry->server && 
413           !strncasecmp(server, entry->server, strlen(server)))
414         break;
415       
416       if (num && entry->num == num)
417         break;
418
419       if (!silc_idcache_list_next(list, &id_cache)) {
420         entry = NULL;
421         break;
422       }
423
424       entry = (SilcClientEntry)id_cache->context;
425     }
426
427     /* If match weren't found, request it */
428     if (!entry)
429       goto identify;
430   }
431
432   if (list)
433     silc_idcache_list_free(list);
434
435   return entry;
436 }
437
438 /* Finds entry for client by the client's ID. Returns the entry or NULL
439    if the entry was not found. */
440
441 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
442                                              SilcClientConnection conn,
443                                              SilcClientID *client_id)
444 {
445   SilcIDCacheEntry id_cache;
446
447   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
448                   silc_id_render(client_id, SILC_ID_CLIENT)));
449
450   /* Find ID from cache */
451   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
452                                        NULL, NULL, 
453                                        silc_hash_client_id_compare, NULL,
454                                        &id_cache))
455     return NULL;
456
457   SILC_LOG_DEBUG(("Found"));
458
459   return (SilcClientEntry)id_cache->context;
460 }
461
462 typedef struct {
463   SilcClient client;
464   SilcClientConnection conn;
465   SilcClientID *client_id;
466   SilcGetClientCallback completion;
467   void *context;
468   int found;
469 } *GetClientByIDInternal;
470
471 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
472 {
473   GetClientByIDInternal i = (GetClientByIDInternal)context;
474   SilcClientEntry entry;
475
476   /* Get the client */
477   entry = silc_client_get_client_by_id(i->client, i->conn,
478                                        i->client_id);
479   if (entry) {
480     i->completion(i->client, i->conn, &entry, 1, i->context);
481     i->found = TRUE;
482   }
483 }
484
485 static void silc_client_get_client_by_id_destructor(void *context)
486 {
487   GetClientByIDInternal i = (GetClientByIDInternal)context;
488
489   if (i->found == FALSE)
490     i->completion(i->client, i->conn, NULL, 0, i->context);
491
492   if (i->client_id)
493     silc_free(i->client_id);
494   silc_free(i);
495 }
496
497 /* Same as above but will always resolve the information from the server.
498    Use this only if you know that you don't have the entry and the only
499    thing you know about the client is its ID. */
500
501 void silc_client_get_client_by_id_resolve(SilcClient client,
502                                           SilcClientConnection conn,
503                                           SilcClientID *client_id,
504                                           SilcGetClientCallback completion,
505                                           void *context)
506 {
507   SilcBuffer idp;
508   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
509
510   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
511   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, 
512                            ++conn->cmd_ident,
513                            1, 3, idp->data, idp->len);
514   silc_buffer_free(idp);
515
516   i->client = client;
517   i->conn = conn;
518   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
519   i->completion = completion;
520   i->context = context;
521       
522   /* Add pending callback */
523   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, 
524                               conn->cmd_ident, 
525                               silc_client_get_client_by_id_destructor,
526                               silc_client_command_get_client_by_id_callback, 
527                               (void *)i);
528 }
529
530 /* Deletes the client entry and frees all memory. */
531
532 void silc_client_del_client_entry(SilcClient client, 
533                                   SilcClientEntry client_entry)
534 {
535   silc_free(client_entry->nickname);
536   silc_free(client_entry->username);
537   silc_free(client_entry->realname);
538   silc_free(client_entry->server);
539   silc_free(client_entry->id);
540   if (client_entry->send_key)
541     silc_cipher_free(client_entry->send_key);
542   if (client_entry->receive_key)
543     silc_cipher_free(client_entry->receive_key);
544   silc_free(client_entry->key);
545   silc_free(client_entry);
546 }
547
548 /* Removes client from the cache by the client entry. */
549
550 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
551                             SilcClientEntry client_entry)
552 {
553   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
554   silc_client_del_client_entry(client, client_entry);
555   return ret;
556 }
557
558 /* Removes channel from the cache by the channel entry. */
559
560 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
561                              SilcChannelEntry channel)
562 {
563   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
564   silc_free(channel->channel_name);
565   silc_free(channel->id);
566   silc_free(channel->key);
567   if (channel->channel_key)
568     silc_cipher_free(channel->channel_key);
569   if (channel->hmac)
570     silc_hmac_free(channel->hmac);
571   silc_client_del_channel_private_keys(client, conn, channel);
572   silc_free(channel);
573   return ret;
574 }
575
576 /* Finds entry for channel by the channel name. Returns the entry or NULL
577    if the entry was not found. It is found only if the client is joined
578    to the channel. */
579
580 SilcChannelEntry silc_client_get_channel(SilcClient client,
581                                          SilcClientConnection conn,
582                                          char *channel)
583 {
584   SilcIDCacheEntry id_cache;
585   SilcChannelEntry entry;
586
587   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
588                                      &id_cache))
589     return NULL;
590
591   entry = (SilcChannelEntry)id_cache->context;
592
593   return entry;
594 }
595
596 /* Finds entry for channel by the channel ID. Returns the entry or NULL
597    if the entry was not found. It is found only if the client is joined
598    to the channel. */
599
600 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
601                                                SilcClientConnection conn,
602                                                SilcChannelID *channel_id)
603 {
604   SilcIDCacheEntry id_cache;
605   SilcChannelEntry entry;
606
607   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
608                                    &id_cache))
609     return NULL;
610
611   entry = (SilcChannelEntry)id_cache->context;
612
613   return entry;
614 }
615
616 typedef struct {
617   SilcClient client;
618   SilcClientConnection conn;
619   SilcChannelID *channel_id;
620   SilcGetChannelCallback completion;
621   void *context;
622   int found;
623 } *GetChannelByIDInternal;
624
625 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
626 {
627   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
628   SilcChannelEntry entry;
629
630   /* Get the channel */
631   entry = silc_client_get_channel_by_id(i->client, i->conn,
632                                         i->channel_id);
633   if (entry) {
634     i->completion(i->client, i->conn, &entry, 1, i->context);
635     i->found = TRUE;
636   }
637 }
638
639 static void silc_client_get_channel_by_id_destructor(void *context)
640 {
641   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
642
643   if (i->found == FALSE)
644     i->completion(i->client, i->conn, NULL, 0, i->context);
645
646   silc_free(i->channel_id);
647   silc_free(i);
648 }
649
650 /* Resolves channel information from the server by the channel ID. */
651
652 void silc_client_get_channel_by_id_resolve(SilcClient client,
653                                            SilcClientConnection conn,
654                                            SilcChannelID *channel_id,
655                                            SilcGetChannelCallback completion,
656                                            void *context)
657 {
658   SilcBuffer idp;
659   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
660
661   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
662   silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
663                            ++conn->cmd_ident,
664                            1, 5, idp->data, idp->len);
665   silc_buffer_free(idp);
666
667   i->client = client;
668   i->conn = conn;
669   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
670   i->completion = completion;
671   i->context = context;
672       
673   /* Add pending callback */
674   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
675                               conn->cmd_ident, 
676                               silc_client_get_channel_by_id_destructor,
677                               silc_client_command_get_channel_by_id_callback, 
678                               (void *)i);
679 }
680
681 /* Find channel entry by ID. This routine is used internally by the library. */
682
683 SilcChannelEntry silc_idlist_get_channel_by_id(SilcClient client,
684                                                SilcClientConnection conn,
685                                                SilcChannelID *channel_id,
686                                                int query)
687 {
688   SilcBuffer idp;
689   SilcChannelEntry channel;
690
691   channel = silc_client_get_channel_by_id(client, conn, channel_id);
692   if (channel)
693     return channel;
694
695   if (query) {
696     idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
697     silc_client_send_command(client, conn, SILC_COMMAND_IDENTIFY, 
698                              ++conn->cmd_ident,
699                              1, 5, idp->data, idp->len);
700     silc_buffer_free(idp);
701   }
702
703   return NULL;
704 }
705
706 /* Finds entry for server by the server name. */
707
708 SilcServerEntry silc_client_get_server(SilcClient client,
709                                        SilcClientConnection conn,
710                                        char *server_name)
711 {
712   SilcIDCacheEntry id_cache;
713   SilcServerEntry entry;
714
715   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
716                                      &id_cache))
717     return NULL;
718
719   entry = (SilcServerEntry)id_cache->context;
720
721   return entry;
722 }
723
724 /* Finds entry for server by the server ID. */
725
726 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
727                                              SilcClientConnection conn,
728                                              SilcServerID *server_id)
729 {
730   SilcIDCacheEntry id_cache;
731   SilcServerEntry entry;
732
733   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
734                                    &id_cache))
735     return NULL;
736
737   entry = (SilcServerEntry)id_cache->context;
738
739   return entry;
740 }
741
742 /* Removes server from the cache by the server entry. */
743
744 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
745                             SilcServerEntry server)
746 {
747   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
748   silc_free(server->server_name);
749   silc_free(server->server_info);
750   silc_free(server->server_id);
751   silc_free(server);
752   return ret;
753 }