Moved the SilcClient and SilcClientConnection to the public
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /******************************************************************************
26
27                          Client Searching Locally
28
29 ******************************************************************************/
30
31 /* Same as silc_client_get_clients function but does not resolve anything
32    from the server. This checks local cache and returns all matching
33    clients from the local cache. If none was found this returns NULL.
34    The `nickname' is the real nickname of the client, and the `format'
35    is the formatted nickname to find exact match from multiple found
36    entries. The format must be same as given in the SilcClientParams
37    structure to the client library. If the `format' is NULL all found
38    clients by `nickname' are returned. */
39
40 SilcClientEntry *silc_client_get_clients_local(SilcClient client,
41                                                SilcClientConnection conn,
42                                                const char *nickname,
43                                                const char *format,
44                                                SilcUInt32 *clients_count)
45 {
46   SilcIDCacheEntry id_cache;
47   SilcIDCacheList list = NULL;
48   SilcClientEntry entry, *clients;
49   int i = 0;
50   bool found = FALSE;
51
52   /* Find ID from cache */
53   if (!silc_idcache_find_by_name(conn->internal->client_cache,
54                                  (char *)nickname, &list))
55     return NULL;
56
57   if (!silc_idcache_list_count(list)) {
58     silc_idcache_list_free(list);
59     return NULL;
60   }
61
62   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
63   *clients_count = silc_idcache_list_count(list);
64
65   if (!format) {
66     /* Take all without any further checking */
67     silc_idcache_list_first(list, &id_cache);
68     while (id_cache) {
69       clients[i++] = id_cache->context;
70       found = TRUE;
71       if (!silc_idcache_list_next(list, &id_cache))
72         break;
73     }
74   } else {
75     /* Check multiple cache entries for match */
76     silc_idcache_list_first(list, &id_cache);
77     while (id_cache) {
78       entry = (SilcClientEntry)id_cache->context;
79       if (strcasecmp(entry->nickname, format)) {
80         if (!silc_idcache_list_next(list, &id_cache)) {
81           break;
82         } else {
83           continue;
84         }
85       }
86       
87       clients[i++] = id_cache->context;
88       found = TRUE;
89       if (!silc_idcache_list_next(list, &id_cache))
90         break;
91     }
92   }
93
94   if (list)
95     silc_idcache_list_free(list);
96
97   if (!found) {
98     *clients_count = 0;
99     if (clients)
100       silc_free(clients);
101     return NULL;
102   }
103
104   return clients;
105 }
106
107
108 /******************************************************************************
109
110                         Client Resolving from Server
111
112 ******************************************************************************/
113
114 typedef struct {
115   SilcClient client;
116   SilcClientConnection conn;
117   SilcGetClientCallback completion;
118   void *context;
119   char *nickname;
120   char *server;
121 } *GetClientInternal;
122
123 SILC_CLIENT_CMD_FUNC(get_client_callback)
124 {
125   GetClientInternal i = (GetClientInternal)context;
126   SilcClientEntry *clients;
127   SilcUInt32 clients_count;
128
129   /* Get the clients */
130   clients = silc_client_get_clients_local(i->client, i->conn,
131                                           i->nickname, i->server,
132                                           &clients_count);
133   if (clients) {
134     i->completion(i->client, i->conn, clients, clients_count, i->context);
135     silc_free(clients);
136   } else {
137     i->completion(i->client, i->conn, NULL, 0, i->context);
138   }
139
140   silc_free(i->nickname);
141   silc_free(i->server);
142   silc_free(i);
143 }
144
145 /* Finds client entry or entries by the `nickname' and `server'. The 
146    completion callback will be called when the client entries has been found.
147
148    Note: this function is always asynchronous and resolves the client
149    information from the server. Thus, if you already know the client
150    information then use the silc_client_get_client_by_id function to
151    get the client entry since this function may be very slow and should
152    be used only to initially get the client entries. */
153
154 void silc_client_get_clients(SilcClient client,
155                              SilcClientConnection conn,
156                              const char *nickname,
157                              const char *server,
158                              SilcGetClientCallback completion,
159                              void *context)
160 {
161   GetClientInternal i;
162   int len;
163   char *userhost;
164
165   if (!nickname)
166     return;
167
168   i = silc_calloc(1, sizeof(*i));
169   i->client = client;
170   i->conn = conn;
171   i->nickname = strdup(nickname);
172   i->server = server ? strdup(server) : NULL;
173   i->completion = completion;
174   i->context = context;
175
176   if (nickname && server) {
177     len = strlen(nickname) + strlen(server) + 3;
178     userhost = silc_calloc(len, sizeof(*userhost));
179     silc_strncat(userhost, len, nickname, strlen(nickname));
180     silc_strncat(userhost, len, "@", 1);
181     silc_strncat(userhost, len, server, strlen(server));
182   } else {
183     userhost = silc_memdup(nickname, strlen(nickname));
184   }
185
186   /* Register our own command reply for this command */
187   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
188                                silc_client_command_reply_identify_i, 0,
189                                ++conn->cmd_ident);
190
191   /* Send the command */
192   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
193                            conn->cmd_ident, 1, 1, userhost, 
194                            strlen(userhost));
195
196   /* Add pending callback */
197   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
198                               silc_client_command_get_client_callback, 
199                               (void *)i);
200
201   silc_free(userhost);
202 }
203
204 /* The old style function to find client entry. This is used by the
205    library internally. If `query' is TRUE then the client information is
206    requested by the server. The pending command callback must be set
207    by the caller. */
208 /* XXX This function should be removed */
209
210 SilcClientEntry silc_idlist_get_client(SilcClient client,
211                                        SilcClientConnection conn,
212                                        const char *nickname,
213                                        const char *format,
214                                        bool query)
215 {
216   SilcIDCacheEntry id_cache;
217   SilcIDCacheList list = NULL;
218   SilcClientEntry entry = NULL;
219
220   SILC_LOG_DEBUG(("Start"));
221
222   /* Find ID from cache */
223   if (!silc_idcache_find_by_name(conn->internal->client_cache,
224                                  (char *)nickname, &list)) {
225   identify:
226
227     if (query) {
228       SILC_LOG_DEBUG(("Requesting Client ID from server"));
229       
230       /* Register our own command reply for this command */
231       silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
232                                    silc_client_command_reply_identify_i, 0,
233                                    ++conn->cmd_ident);
234
235       /* Send the command */
236       silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
237                                conn->cmd_ident, 1, 1, nickname,
238                                strlen(nickname));
239
240       if (list)
241         silc_idcache_list_free(list);
242
243       return NULL;
244     }
245     return NULL;
246   }
247
248   if (!format) {
249     /* Take first found cache entry */
250     if (!silc_idcache_list_first(list, &id_cache))
251       goto identify;
252
253     entry = (SilcClientEntry)id_cache->context;
254   } else {
255     /* Check multiple cache entries for match */
256     silc_idcache_list_first(list, &id_cache);
257     while (id_cache) {
258       entry = (SilcClientEntry)id_cache->context;
259
260       if (strcasecmp(entry->nickname, format)) {
261         if (!silc_idcache_list_next(list, &id_cache)) {
262           entry = NULL;
263           break;
264         } else {
265           entry = NULL;
266           continue;
267         }
268       }
269
270       break;
271     }
272
273     /* If match weren't found, request it */
274     if (!entry)
275       goto identify;
276   }
277
278   if (list)
279     silc_idcache_list_free(list);
280
281   return entry;
282 }
283
284
285 typedef struct {
286   SilcClient client;
287   SilcClientConnection conn;
288   SilcUInt32 list_count;
289   SilcBuffer client_id_list;
290   SilcGetClientCallback completion;
291   void *context;
292   int res_count;
293 } *GetClientsByListInternal;
294
295 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
296 {
297   GetClientsByListInternal i = (GetClientsByListInternal)context;
298   SilcIDCacheEntry id_cache = NULL;
299   SilcBuffer client_id_list = i->client_id_list;
300   SilcClientEntry *clients = NULL;
301   SilcUInt32 clients_count = 0;
302   bool found = FALSE;
303   int c;
304
305   SILC_LOG_DEBUG(("Start"));
306
307   if (i->res_count) {
308     i->res_count--;
309     if (i->res_count)
310       return;
311   }
312
313   SILC_LOG_DEBUG(("Resolved all clients"));
314
315   for (c = 0; c < i->list_count; c++) {
316     SilcUInt16 idp_len;
317     SilcClientID *client_id;
318
319     /* Get Client ID */
320     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
321     idp_len += 4;
322     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
323     if (!client_id) {
324       silc_buffer_pull(client_id_list, idp_len);
325       continue;
326     }
327
328     /* Get the client entry */
329     if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache, 
330                                         (void *)client_id, 
331                                         NULL, NULL, 
332                                         silc_hash_client_id_compare, NULL,
333                                         &id_cache)) {
334       clients = silc_realloc(clients, sizeof(*clients) * 
335                              (clients_count + 1));
336       clients[clients_count] = (SilcClientEntry)id_cache->context;
337       clients_count++;
338       found = TRUE;
339     }
340
341     silc_free(client_id);
342     silc_buffer_pull(client_id_list, idp_len);
343   }
344
345   if (found) {
346     i->completion(i->client, i->conn, clients, clients_count, i->context);
347     silc_free(clients);
348   } else {
349     i->completion(i->client, i->conn, NULL, 0, i->context);
350   }
351
352   if (i->client_id_list)
353     silc_buffer_free(i->client_id_list);
354   silc_free(i);
355 }
356
357 /* Gets client entries by the list of client ID's `client_id_list'. This
358    always resolves those client ID's it does not know yet from the server
359    so this function might take a while. The `client_id_list' is a list
360    of ID Payloads added one after other.  JOIN command reply and USERS
361    command reply for example returns this sort of list. The `completion'
362    will be called after the entries are available. */
363
364 void silc_client_get_clients_by_list(SilcClient client,
365                                      SilcClientConnection conn,
366                                      SilcUInt32 list_count,
367                                      SilcBuffer client_id_list,
368                                      SilcGetClientCallback completion,
369                                      void *context)
370 {
371   SilcIDCacheEntry id_cache = NULL;
372   int i;
373   unsigned char **res_argv = NULL;
374   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
375   GetClientsByListInternal in;
376   bool wait_res = FALSE;
377
378   SILC_LOG_DEBUG(("Start"));
379
380   in = silc_calloc(1, sizeof(*in));
381   in->client = client;
382   in->conn = conn;
383   in->list_count = list_count;
384   in->client_id_list = silc_buffer_copy(client_id_list);
385   in->completion = completion;
386   in->context = context;
387
388   for (i = 0; i < list_count; i++) {
389     SilcUInt16 idp_len;
390     SilcClientID *client_id;
391     SilcClientEntry entry;
392     bool ret;
393
394     /* Get Client ID */
395     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
396     idp_len += 4;
397     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
398     if (!client_id) {
399       silc_buffer_pull(client_id_list, idp_len);
400       continue;
401     }
402
403     /* Check if we have this client cached already. */
404     ret =
405       silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
406                                       (void *)client_id, NULL, NULL, 
407                                       silc_hash_client_id_compare, NULL,
408                                       &id_cache);
409
410     /* If we don't have the entry or it has incomplete info, then resolve
411        it from the server. */
412     if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
413       entry = ret ? (SilcClientEntry)id_cache->context : NULL;
414
415       if (entry) {
416         if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
417           /* Attach to this resolving and wait until it finishes */
418           silc_client_command_pending(
419                             conn, SILC_COMMAND_NONE, 
420                             entry->resolve_cmd_ident,
421                             silc_client_command_get_clients_list_callback, 
422                             (void *)in);
423           wait_res = TRUE;
424           in->res_count++;
425
426           silc_free(client_id);
427           silc_buffer_pull(client_id_list, idp_len);
428           continue;
429         }
430
431         entry->status |= SILC_CLIENT_STATUS_RESOLVING;
432         entry->resolve_cmd_ident = conn->cmd_ident + 1;
433       }
434
435       /* No we don't have it, query it from the server. Assemble argument
436          table that will be sent for the IDENTIFY command later. */
437       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
438                               (res_argc + 1));
439       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
440                                    (res_argc + 1));
441       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
442                                     (res_argc + 1));
443       res_argv[res_argc] = client_id_list->data;
444       res_argv_lens[res_argc] = idp_len;
445       res_argv_types[res_argc] = res_argc + 5;
446       res_argc++;
447     }
448
449     silc_free(client_id);
450     silc_buffer_pull(client_id_list, idp_len);
451   }
452
453   silc_buffer_push(client_id_list, client_id_list->data - 
454                    client_id_list->head);
455
456   /* Query the client information from server if the list included clients
457      that we don't know about. */
458   if (res_argc) {
459     SilcBuffer res_cmd;
460
461     /* Send the IDENTIFY command to server */
462     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
463                                           res_argc, res_argv, res_argv_lens,
464                                           res_argv_types, ++conn->cmd_ident);
465     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
466                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
467                             TRUE);
468
469     /* Register our own command reply for this command */
470     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
471                                  silc_client_command_reply_identify_i, 0,
472                                  conn->cmd_ident);
473
474     /* Process the applications request after reply has been received  */
475     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
476                                 silc_client_command_get_clients_list_callback, 
477                                 (void *)in);
478     in->res_count++;
479
480     silc_buffer_free(res_cmd);
481     silc_free(res_argv);
482     silc_free(res_argv_lens);
483     silc_free(res_argv_types);
484     return;
485   }
486
487   if (wait_res)
488     return;
489
490   /* We have the clients in cache, get them and call the completion */
491   silc_client_command_get_clients_list_callback((void *)in, NULL);
492 }
493
494 /* Finds entry for client by the client's ID. Returns the entry or NULL
495    if the entry was not found. */
496
497 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
498                                              SilcClientConnection conn,
499                                              SilcClientID *client_id)
500 {
501   SilcIDCacheEntry id_cache;
502
503   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
504                   silc_id_render(client_id, SILC_ID_CLIENT)));
505
506   /* Find ID from cache */
507   if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
508                                        (void *)client_id, NULL, NULL, 
509                                        silc_hash_client_id_compare, NULL,
510                                        &id_cache))
511     return NULL;
512
513   SILC_LOG_DEBUG(("Found"));
514
515   return (SilcClientEntry)id_cache->context;
516 }
517
518 typedef struct {
519   SilcClient client;
520   SilcClientConnection conn;
521   SilcClientID *client_id;
522   SilcGetClientCallback completion;
523   void *context;
524 } *GetClientByIDInternal;
525
526 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
527 {
528   GetClientByIDInternal i = (GetClientByIDInternal)context;
529   SilcClientEntry entry;
530
531   /* Get the client */
532   entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
533   if (entry) {
534     if (i->completion)
535       i->completion(i->client, i->conn, &entry, 1, i->context);
536   } else {
537     if (i->completion)
538       i->completion(i->client, i->conn, NULL, 0, i->context);
539   }
540
541   silc_free(i->client_id);
542   silc_free(i);
543 }
544
545 /* Same as above but will always resolve the information from the server.
546    Use this only if you know that you don't have the entry and the only
547    thing you know about the client is its ID. */
548
549 void silc_client_get_client_by_id_resolve(SilcClient client,
550                                           SilcClientConnection conn,
551                                           SilcClientID *client_id,
552                                           SilcBuffer attributes,
553                                           SilcGetClientCallback completion,
554                                           void *context)
555 {
556   SilcBuffer idp;
557   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
558
559   SILC_LOG_DEBUG(("Start"));
560
561   i->client = client;
562   i->conn = conn;
563   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
564   i->completion = completion;
565   i->context = context;
566       
567   /* Register our own command reply for this command */
568   silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
569                                silc_client_command_reply_whois_i, 0,
570                                ++conn->cmd_ident);
571
572   /* Send the command */
573   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
574   silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
575                            2, 3, attributes ? attributes->data : NULL,
576                            attributes ? attributes->len : 0,
577                            4, idp->data, idp->len);
578   silc_buffer_free(idp);
579
580   /* Add pending callback */
581   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
582                               silc_client_command_get_client_by_id_callback, 
583                               (void *)i);
584 }
585
586
587 /******************************************************************************
588
589                 Client, Channel and Server entry manipulation
590
591 ******************************************************************************/
592
593
594 /* Creates new client entry and adds it to the ID cache. Returns pointer
595    to the new entry. */
596
597 SilcClientEntry
598 silc_client_add_client(SilcClient client, SilcClientConnection conn,
599                        char *nickname, char *username, 
600                        char *userinfo, SilcClientID *id, SilcUInt32 mode)
601 {
602   SilcClientEntry client_entry;
603   char *nick = NULL;
604
605   SILC_LOG_DEBUG(("Start"));
606
607   /* Save the client infos */
608   client_entry = silc_calloc(1, sizeof(*client_entry));
609   client_entry->id = id;
610   client_entry->valid = TRUE;
611   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
612   silc_parse_userfqdn(username, &client_entry->username, 
613                       &client_entry->hostname);
614   if (userinfo)
615     client_entry->realname = strdup(userinfo);
616   client_entry->mode = mode;
617   if (nick)
618     client_entry->nickname = strdup(nick);
619   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
620                                                  NULL, NULL, NULL, TRUE);
621
622   /* Format the nickname */
623   silc_client_nickname_format(client, conn, client_entry);
624   
625   /* Add client to cache, the non-formatted nickname is saved to cache */
626   if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id, 
627                         (void *)client_entry, 0, NULL)) {
628     silc_free(client_entry->nickname);
629     silc_free(client_entry->username);
630     silc_free(client_entry->hostname);
631     silc_free(client_entry->server);
632     silc_hash_table_free(client_entry->channels);
633     silc_free(client_entry);
634     return NULL;
635   }
636
637   return client_entry;
638 }
639
640 /* Updates the `client_entry' with the new information sent as argument. */
641
642 void silc_client_update_client(SilcClient client,
643                                SilcClientConnection conn,
644                                SilcClientEntry client_entry,
645                                const char *nickname,
646                                const char *username,
647                                const char *userinfo,
648                                SilcUInt32 mode)
649 {
650   char *nick = NULL;
651
652   SILC_LOG_DEBUG(("Start"));
653
654   if ((!client_entry->username || !client_entry->hostname) && username) {
655     silc_free(client_entry->username);
656     silc_free(client_entry->hostname);
657     client_entry->username = NULL;
658     client_entry->hostname = NULL;
659     silc_parse_userfqdn(username, &client_entry->username,
660                         &client_entry->hostname);
661   }
662   if (!client_entry->realname && userinfo)
663     client_entry->realname = strdup(userinfo);
664   if (!client_entry->nickname && nickname) {
665     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
666     client_entry->nickname = strdup(nick);
667     silc_client_nickname_format(client, conn, client_entry);
668   }
669   client_entry->mode = mode;
670
671   if (nick) {
672     /* Remove the old cache entry and create a new one */
673     silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
674     silc_idcache_add(conn->internal->client_cache, nick, client_entry->id, 
675                      client_entry, 0, NULL);
676   }
677 }
678
679 /* Deletes the client entry and frees all memory. */
680
681 void silc_client_del_client_entry(SilcClient client, 
682                                   SilcClientConnection conn,
683                                   SilcClientEntry client_entry)
684 {
685   SILC_LOG_DEBUG(("Start"));
686
687   silc_free(client_entry->nickname);
688   silc_free(client_entry->username);
689   silc_free(client_entry->realname);
690   silc_free(client_entry->hostname);
691   silc_free(client_entry->server);
692   silc_free(client_entry->id);
693   silc_free(client_entry->fingerprint);
694   silc_hash_table_free(client_entry->channels);
695   if (client_entry->send_key)
696     silc_cipher_free(client_entry->send_key);
697   if (client_entry->receive_key)
698     silc_cipher_free(client_entry->receive_key);
699   silc_free(client_entry->key);
700   silc_client_ftp_session_free_client(conn, client_entry);
701   if (client_entry->ke)
702     silc_client_abort_key_agreement(client, conn, client_entry);
703   silc_free(client_entry);
704 }
705
706 /* Removes client from the cache by the client entry. */
707
708 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
709                             SilcClientEntry client_entry)
710 {
711   bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
712                                          client_entry);
713
714   /* Remove from channels */
715   silc_client_remove_from_channels(client, conn, client_entry);
716
717   /* Free the client entry data */
718   silc_client_del_client_entry(client, conn, client_entry);
719
720   return ret;
721 }
722
723 /* Add new channel entry to the ID Cache */
724
725 SilcChannelEntry silc_client_add_channel(SilcClient client,
726                                          SilcClientConnection conn,
727                                          const char *channel_name,
728                                          SilcUInt32 mode, 
729                                          SilcChannelID *channel_id)
730 {
731   SilcChannelEntry channel;
732
733   SILC_LOG_DEBUG(("Start"));
734
735   channel = silc_calloc(1, sizeof(*channel));
736   channel->channel_name = strdup(channel_name);
737   channel->id = channel_id;
738   channel->mode = mode;
739   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
740                                              NULL, NULL, NULL, TRUE);
741
742   /* Put it to the ID cache */
743   if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name, 
744                         (void *)channel->id, (void *)channel, 0, NULL)) {
745     silc_free(channel->channel_name);
746     silc_hash_table_free(channel->user_list);
747     silc_free(channel);
748     return NULL;
749   }
750
751   return channel;
752 }
753
754 /* Foreach callbcak to free all users from the channel when deleting a
755    channel entry. */
756
757 static void silc_client_del_channel_foreach(void *key, void *context,
758                                             void *user_context)
759 {
760   SilcChannelUser chu = (SilcChannelUser)context;
761
762   SILC_LOG_DEBUG(("Start"));
763
764   /* Remove the context from the client's channel hash table as that
765      table and channel's user_list hash table share this same context. */
766   silc_hash_table_del(chu->client->channels, chu->channel);
767   silc_free(chu);
768 }
769
770 /* Removes channel from the cache by the channel entry. */
771
772 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
773                              SilcChannelEntry channel)
774 {
775   bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
776                                          channel);
777
778   SILC_LOG_DEBUG(("Start"));
779
780   /* Free all client entrys from the users list. The silc_hash_table_free
781      will free all the entries so they are not freed at the foreach 
782      callback. */
783   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
784                           NULL);
785   silc_hash_table_free(channel->user_list);
786
787   silc_free(channel->channel_name);
788   silc_free(channel->id);
789   silc_free(channel->key);
790   if (channel->channel_key)
791     silc_cipher_free(channel->channel_key);
792   if (channel->hmac)
793     silc_hmac_free(channel->hmac);
794   if (channel->old_channel_key)
795     silc_cipher_free(channel->old_channel_key);
796   if (channel->old_hmac)
797     silc_hmac_free(channel->old_hmac);
798   if (channel->rekey_task)
799     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
800   silc_client_del_channel_private_keys(client, conn, channel);
801   silc_free(channel);
802   return ret;
803 }
804
805 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
806    if the ID could not be changed. */
807
808 bool silc_client_replace_channel_id(SilcClient client,
809                                     SilcClientConnection conn,
810                                     SilcChannelEntry channel,
811                                     SilcChannelID *new_id)
812 {
813   if (!new_id)
814     return FALSE;
815
816   SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
817                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
818   SILC_LOG_DEBUG(("New Channel ID id(%s)", 
819                   silc_id_render(new_id, SILC_ID_CHANNEL)));
820
821   silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
822   silc_free(channel->id);
823   channel->id = new_id;
824   return silc_idcache_add(conn->internal->channel_cache,
825                           channel->channel_name, 
826                           (void *)channel->id, (void *)channel, 0, NULL);
827
828 }
829
830 /* Finds entry for channel by the channel name. Returns the entry or NULL
831    if the entry was not found. It is found only if the client is joined
832    to the channel. */
833
834 SilcChannelEntry silc_client_get_channel(SilcClient client,
835                                          SilcClientConnection conn,
836                                          char *channel)
837 {
838   SilcIDCacheEntry id_cache;
839   SilcChannelEntry entry;
840
841   SILC_LOG_DEBUG(("Start"));
842
843   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel, 
844                                      &id_cache))
845     return NULL;
846
847   entry = (SilcChannelEntry)id_cache->context;
848
849   SILC_LOG_DEBUG(("Found"));
850
851   return entry;
852 }
853
854 /* Finds entry for channel by the channel ID. Returns the entry or NULL
855    if the entry was not found. It is found only if the client is joined
856    to the channel. */
857
858 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
859                                                SilcClientConnection conn,
860                                                SilcChannelID *channel_id)
861 {
862   SilcIDCacheEntry id_cache;
863   SilcChannelEntry entry;
864
865   SILC_LOG_DEBUG(("Start"));
866
867   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id, 
868                                    &id_cache))
869     return NULL;
870
871   entry = (SilcChannelEntry)id_cache->context;
872
873   SILC_LOG_DEBUG(("Found"));
874
875   return entry;
876 }
877
878 typedef struct {
879   SilcClient client;
880   SilcClientConnection conn;
881   SilcChannelID *channel_id;
882   SilcGetChannelCallback completion;
883   void *context;
884 } *GetChannelByIDInternal;
885
886 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
887 {
888   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
889   SilcChannelEntry entry;
890
891   SILC_LOG_DEBUG(("Start"));
892
893   /* Get the channel */
894   entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
895   if (entry) {
896     i->completion(i->client, i->conn, &entry, 1, i->context);
897   } else {
898     i->completion(i->client, i->conn, NULL, 0, i->context);
899   }
900
901   silc_free(i->channel_id);
902   silc_free(i);
903 }
904
905 /* Resolves channel information from the server by the channel ID. */
906
907 void silc_client_get_channel_by_id_resolve(SilcClient client,
908                                            SilcClientConnection conn,
909                                            SilcChannelID *channel_id,
910                                            SilcGetChannelCallback completion,
911                                            void *context)
912 {
913   SilcBuffer idp;
914   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
915
916   SILC_LOG_DEBUG(("Start"));
917
918   i->client = client;
919   i->conn = conn;
920   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
921   i->completion = completion;
922   i->context = context;
923       
924   /* Register our own command reply for this command */
925   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
926                                silc_client_command_reply_identify_i, 0,
927                                ++conn->cmd_ident);
928
929   /* Send the command */
930   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
931   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
932                            conn->cmd_ident,
933                            1, 5, idp->data, idp->len);
934   silc_buffer_free(idp);
935
936   /* Add pending callback */
937   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
938                               silc_client_command_get_channel_by_id_callback, 
939                               (void *)i);
940 }
941
942 /* Finds entry for server by the server name. */
943
944 SilcServerEntry silc_client_get_server(SilcClient client,
945                                        SilcClientConnection conn,
946                                        char *server_name)
947 {
948   SilcIDCacheEntry id_cache;
949   SilcServerEntry entry;
950
951   SILC_LOG_DEBUG(("Start"));
952
953   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
954                                      server_name, &id_cache))
955     return NULL;
956
957   entry = (SilcServerEntry)id_cache->context;
958
959   return entry;
960 }
961
962 /* Finds entry for server by the server ID. */
963
964 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
965                                              SilcClientConnection conn,
966                                              SilcServerID *server_id)
967 {
968   SilcIDCacheEntry id_cache;
969   SilcServerEntry entry;
970
971   SILC_LOG_DEBUG(("Start"));
972
973   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
974                                    (void *)server_id, &id_cache))
975     return NULL;
976
977   entry = (SilcServerEntry)id_cache->context;
978
979   return entry;
980 }
981
982 /* Add new server entry */
983
984 SilcServerEntry silc_client_add_server(SilcClient client,
985                                        SilcClientConnection conn,
986                                        const char *server_name,
987                                        const char *server_info,
988                                        SilcServerID *server_id)
989 {
990   SilcServerEntry server_entry;
991
992   SILC_LOG_DEBUG(("Start"));
993
994   server_entry = silc_calloc(1, sizeof(*server_entry));
995   if (!server_entry || !server_id)
996     return NULL;
997
998   server_entry->server_id = server_id;
999   if (server_name)
1000     server_entry->server_name = strdup(server_name);
1001   if (server_info)
1002     server_entry->server_info = strdup(server_info);
1003
1004   /* Add server to cache */
1005   if (!silc_idcache_add(conn->internal->server_cache,
1006                         server_entry->server_name,
1007                         server_entry->server_id, server_entry, 0, NULL)) {
1008     silc_free(server_entry->server_id);
1009     silc_free(server_entry->server_name);
1010     silc_free(server_entry->server_info);
1011     silc_free(server_entry);
1012     return NULL;
1013   }
1014
1015   return server_entry;
1016 }
1017
1018 /* Removes server from the cache by the server entry. */
1019
1020 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1021                             SilcServerEntry server)
1022 {
1023   bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1024   silc_free(server->server_name);
1025   silc_free(server->server_info);
1026   silc_free(server->server_id);
1027   silc_free(server);
1028   return ret;
1029 }
1030
1031 /* Updates the `server_entry' with the new information sent as argument. */
1032
1033 void silc_client_update_server(SilcClient client,
1034                                SilcClientConnection conn,
1035                                SilcServerEntry server_entry,
1036                                const char *server_name,
1037                                const char *server_info)
1038 {
1039   SILC_LOG_DEBUG(("Start"));
1040
1041   if (server_name && (!server_entry->server_name ||
1042                       strcmp(server_entry->server_name, server_name))) {
1043
1044     silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1045     silc_free(server_entry->server_name);
1046     server_entry->server_name = strdup(server_name);
1047     silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1048                      server_entry->server_id,
1049                      server_entry, 0, NULL);
1050   }
1051
1052   if (server_info && (!server_entry->server_info ||
1053                       strcmp(server_entry->server_info, server_info))) {
1054     silc_free(server_entry->server_info);
1055     server_entry->server_info = strdup(server_info);
1056   }
1057 }
1058
1059 /* Formats the nickname of the client specified by the `client_entry'.
1060    If the format is specified by the application this will format the
1061    nickname and replace the old nickname in the client entry. If the
1062    format string is not specified then this function has no effect. */
1063
1064 void silc_client_nickname_format(SilcClient client, 
1065                                  SilcClientConnection conn,
1066                                  SilcClientEntry client_entry)
1067 {
1068   char *cp;
1069   char *newnick = NULL;
1070   int i, off = 0, len;
1071   bool freebase;
1072   SilcClientEntry *clients;
1073   SilcUInt32 clients_count = 0;
1074   SilcClientEntry unformatted = NULL;
1075
1076   SILC_LOG_DEBUG(("Start"));
1077
1078   if (!client->internal->params->nickname_format[0])
1079     return;
1080
1081   if (!client_entry->nickname)
1082     return;
1083
1084   /* Get all clients with same nickname. Do not perform the formatting
1085      if there aren't any clients with same nickname unless the application
1086      is forcing us to do so. */
1087   clients = silc_client_get_clients_local(client, conn,
1088                                           client_entry->nickname, NULL,
1089                                           &clients_count);
1090   if (!clients && !client->internal->params->nickname_force_format)
1091     return;
1092
1093   len = 0;
1094   freebase = TRUE;
1095   for (i = 0; i < clients_count; i++) {
1096     if (clients[i]->valid && clients[i] != client_entry)
1097       len++;
1098     if (clients[i]->valid && clients[i] != client_entry &&
1099         !strcmp(clients[i]->nickname, client_entry->nickname))
1100       freebase = FALSE;
1101   }
1102   if (!len || freebase)
1103     return;
1104
1105   cp = client->internal->params->nickname_format;
1106   while (*cp) {
1107     if (*cp == '%') {
1108       cp++;
1109       continue;
1110     }
1111
1112     switch(*cp) {
1113     case 'n':
1114       /* Nickname */
1115       if (!client_entry->nickname)
1116         break;
1117       len = strlen(client_entry->nickname);
1118       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1119       memcpy(&newnick[off], client_entry->nickname, len);
1120       off += len;
1121       break;
1122     case 'h':
1123       /* Stripped hostname */
1124       if (!client_entry->hostname)
1125         break;
1126       len = strcspn(client_entry->hostname, ".");
1127       i = strcspn(client_entry->hostname, "-");
1128       if (i < len)
1129         len = i;
1130       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1131       memcpy(&newnick[off], client_entry->hostname, len);
1132       off += len;
1133       break;
1134     case 'H':
1135       /* Full hostname */
1136       if (!client_entry->hostname)
1137         break;
1138       len = strlen(client_entry->hostname);
1139       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1140       memcpy(&newnick[off], client_entry->hostname, len);
1141       off += len;
1142       break;
1143     case 's':
1144       /* Stripped server name */
1145       if (!client_entry->server)
1146         break;
1147       len = strcspn(client_entry->server, ".");
1148       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1149       memcpy(&newnick[off], client_entry->server, len);
1150       off += len;
1151       break;
1152     case 'S':
1153       /* Full server name */
1154       if (!client_entry->server)
1155         break;
1156       len = strlen(client_entry->server);
1157       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1158       memcpy(&newnick[off], client_entry->server, len);
1159       off += len;
1160       break;
1161     case 'a':
1162       /* Ascending number */
1163       {
1164         char tmp[6];
1165         int num, max = 1;
1166
1167         if (clients_count == 1) {
1168           unformatted = clients[0];
1169           break;
1170         }
1171
1172         for (i = 0; i < clients_count; i++) {
1173           if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1174                            strlen(clients[i]->nickname)))
1175             unformatted = clients[i];
1176           if (strncasecmp(clients[i]->nickname, newnick, off))
1177             continue;
1178           if (strlen(clients[i]->nickname) <= off)
1179             continue;
1180           num = atoi(&clients[i]->nickname[off]);
1181           if (num > max)
1182             max = num;
1183         }
1184
1185         memset(tmp, 0, sizeof(tmp));
1186         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1187         len = strlen(tmp);
1188         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1189         memcpy(&newnick[off], tmp, len);
1190         off += len;
1191       }
1192       break;
1193     default:
1194       /* Some other character in the string */
1195       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1196       memcpy(&newnick[off], cp, 1);
1197       off++;
1198       break;
1199     }
1200
1201     cp++;
1202   }
1203
1204   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1205   newnick[off] = 0;
1206
1207   /* If we are changing nickname of our local entry we'll enforce
1208      that we will always get the unformatted nickname.  Give our
1209      format number to the one that is not formatted now. */
1210   if (unformatted && client_entry == conn->local_entry)
1211     client_entry = unformatted;
1212
1213   silc_free(client_entry->nickname);
1214   client_entry->nickname = newnick;
1215   silc_free(clients);
1216 }