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