Integer type name change.
[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 } *GetClientsByListInternal;
291
292 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
293 {
294   GetClientsByListInternal i = (GetClientsByListInternal)context;
295   SilcIDCacheEntry id_cache = NULL;
296   SilcBuffer client_id_list = i->client_id_list;
297   SilcClientEntry *clients = NULL;
298   SilcUInt32 clients_count = 0;
299   bool found = FALSE;
300   int c;
301
302   SILC_LOG_DEBUG(("Start"));
303
304   for (c = 0; c < i->list_count; c++) {
305     SilcUInt16 idp_len;
306     SilcClientID *client_id;
307
308     /* Get Client ID */
309     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
310     idp_len += 4;
311     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
312     if (!client_id) {
313       silc_buffer_pull(client_id_list, idp_len);
314       continue;
315     }
316
317     /* Get the client entry */
318     if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
319                                         (void *)client_id, 
320                                         NULL, NULL, 
321                                         silc_hash_client_id_compare, NULL,
322                                         &id_cache)) {
323       clients = silc_realloc(clients, sizeof(*clients) * 
324                              (clients_count + 1));
325       clients[clients_count] = (SilcClientEntry)id_cache->context;
326       clients_count++;
327       found = TRUE;
328     }
329
330     silc_free(client_id);
331     silc_buffer_pull(client_id_list, idp_len);
332   }
333
334   if (found) {
335     i->completion(i->client, i->conn, clients, clients_count, i->context);
336     silc_free(clients);
337   } else {
338     i->completion(i->client, i->conn, NULL, 0, i->context);
339   }
340
341   if (i->client_id_list)
342     silc_buffer_free(i->client_id_list);
343   silc_free(i);
344 }
345
346 /* Gets client entries by the list of client ID's `client_id_list'. This
347    always resolves those client ID's it does not know yet from the server
348    so this function might take a while. The `client_id_list' is a list
349    of ID Payloads added one after other.  JOIN command reply and USERS
350    command reply for example returns this sort of list. The `completion'
351    will be called after the entries are available. */
352
353 void silc_client_get_clients_by_list(SilcClient client,
354                                      SilcClientConnection conn,
355                                      SilcUInt32 list_count,
356                                      SilcBuffer client_id_list,
357                                      SilcGetClientCallback completion,
358                                      void *context)
359 {
360   SilcIDCacheEntry id_cache = NULL;
361   int i;
362   unsigned char **res_argv = NULL;
363   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
364   GetClientsByListInternal in;
365
366   SILC_LOG_DEBUG(("Start"));
367
368   in = silc_calloc(1, sizeof(*in));
369   in->client = client;
370   in->conn = conn;
371   in->list_count = list_count;
372   in->client_id_list = silc_buffer_copy(client_id_list);
373   in->completion = completion;
374   in->context = context;
375
376   for (i = 0; i < list_count; i++) {
377     SilcUInt16 idp_len;
378     SilcClientID *client_id;
379     SilcClientEntry entry;
380
381     /* Get Client ID */
382     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
383     idp_len += 4;
384     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
385     if (!client_id) {
386       silc_buffer_pull(client_id_list, idp_len);
387       continue;
388     }
389
390     /* Check if we have this client cached already. */
391     id_cache = NULL;
392     silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
393                                     NULL, NULL, 
394                                     silc_hash_client_id_compare, NULL,
395                                     &id_cache);
396
397     /* If we don't have the entry or it has incomplete info, then resolve
398        it from the server. */
399     entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
400     if (!id_cache || !entry->nickname) {
401
402       if (entry) {
403         if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
404           entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
405           silc_free(client_id);
406           silc_buffer_pull(client_id_list, idp_len);
407           continue;
408         }
409
410         entry->status |= SILC_CLIENT_STATUS_RESOLVING;
411       }
412
413       /* No we don't have it, query it from the server. Assemble argument
414          table that will be sent fr the IDENTIFY command later. */
415       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
416                               (res_argc + 1));
417       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
418                                    (res_argc + 1));
419       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
420                                     (res_argc + 1));
421       res_argv[res_argc] = client_id_list->data;
422       res_argv_lens[res_argc] = idp_len;
423       res_argv_types[res_argc] = res_argc + 5;
424       res_argc++;
425     }
426
427     silc_free(client_id);
428     silc_buffer_pull(client_id_list, idp_len);
429   }
430
431   /* Query the client information from server if the list included clients
432      that we don't know about. */
433   if (res_argc) {
434     SilcBuffer res_cmd;
435
436     /* Send the IDENTIFY command to server */
437     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
438                                           res_argc, res_argv, res_argv_lens,
439                                           res_argv_types, ++conn->cmd_ident);
440     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
441                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
442                             TRUE);
443
444     /* Register our own command reply for this command */
445     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
446                                  silc_client_command_reply_identify_i, 0,
447                                  conn->cmd_ident);
448
449     /* Process the applications request after reply has been received  */
450     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
451                                 silc_client_command_get_clients_list_callback, 
452                                 (void *)in);
453
454     silc_buffer_push(client_id_list, client_id_list->data - 
455                      client_id_list->head);
456     silc_buffer_free(res_cmd);
457     silc_free(res_argv);
458     silc_free(res_argv_lens);
459     silc_free(res_argv_types);
460     return;
461   }
462
463   silc_buffer_push(client_id_list, client_id_list->data - 
464                    client_id_list->head);
465
466   /* We have the clients in cache, get them and call the completion */
467   silc_client_command_get_clients_list_callback((void *)in, NULL);
468 }
469
470 /* Finds entry for client by the client's ID. Returns the entry or NULL
471    if the entry was not found. */
472
473 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
474                                              SilcClientConnection conn,
475                                              SilcClientID *client_id)
476 {
477   SilcIDCacheEntry id_cache;
478
479   SILC_LOG_DEBUG(("Finding client by ID (%s)", 
480                   silc_id_render(client_id, SILC_ID_CLIENT)));
481
482   /* Find ID from cache */
483   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
484                                        NULL, NULL, 
485                                        silc_hash_client_id_compare, NULL,
486                                        &id_cache))
487     return NULL;
488
489   SILC_LOG_DEBUG(("Found"));
490
491   return (SilcClientEntry)id_cache->context;
492 }
493
494 typedef struct {
495   SilcClient client;
496   SilcClientConnection conn;
497   SilcClientID *client_id;
498   SilcGetClientCallback completion;
499   void *context;
500 } *GetClientByIDInternal;
501
502 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
503 {
504   GetClientByIDInternal i = (GetClientByIDInternal)context;
505   SilcClientEntry entry;
506
507   /* Get the client */
508   entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
509   if (entry)
510     i->completion(i->client, i->conn, &entry, 1, i->context);
511   else
512     i->completion(i->client, i->conn, NULL, 0, i->context);
513
514   silc_free(i->client_id);
515   silc_free(i);
516 }
517
518 /* Same as above but will always resolve the information from the server.
519    Use this only if you know that you don't have the entry and the only
520    thing you know about the client is its ID. */
521
522 void silc_client_get_client_by_id_resolve(SilcClient client,
523                                           SilcClientConnection conn,
524                                           SilcClientID *client_id,
525                                           SilcGetClientCallback completion,
526                                           void *context)
527 {
528   SilcBuffer idp;
529   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
530
531   SILC_LOG_DEBUG(("Start"));
532
533   i->client = client;
534   i->conn = conn;
535   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
536   i->completion = completion;
537   i->context = context;
538       
539   /* Register our own command reply for this command */
540   silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
541                                silc_client_command_reply_whois_i, 0,
542                                ++conn->cmd_ident);
543
544   /* Send the command */
545   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
546   silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
547                            1, 3, idp->data, idp->len);
548   silc_buffer_free(idp);
549
550   /* Add pending callback */
551   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
552                               silc_client_command_get_client_by_id_callback, 
553                               (void *)i);
554 }
555
556
557 /******************************************************************************
558
559                 Client, Channel and Server entry manipulation
560
561 ******************************************************************************/
562
563
564 /* Creates new client entry and adds it to the ID cache. Returns pointer
565    to the new entry. */
566
567 SilcClientEntry
568 silc_client_add_client(SilcClient client, SilcClientConnection conn,
569                        char *nickname, char *username, 
570                        char *userinfo, SilcClientID *id, SilcUInt32 mode)
571 {
572   SilcClientEntry client_entry;
573   char *nick = NULL;
574
575   SILC_LOG_DEBUG(("Start"));
576
577   /* Save the client infos */
578   client_entry = silc_calloc(1, sizeof(*client_entry));
579   client_entry->id = id;
580   client_entry->valid = TRUE;
581   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
582   silc_parse_userfqdn(username, &client_entry->username, 
583                       &client_entry->hostname);
584   if (userinfo)
585     client_entry->realname = strdup(userinfo);
586   client_entry->mode = mode;
587   if (nick)
588     client_entry->nickname = strdup(nick);
589   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
590                                                  NULL, NULL, NULL, TRUE);
591
592   /* Format the nickname */
593   silc_client_nickname_format(client, conn, client_entry);
594   
595   /* Add client to cache, the non-formatted nickname is saved to cache */
596   if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, 
597                         (void *)client_entry, 0, NULL)) {
598     silc_free(client_entry->nickname);
599     silc_free(client_entry->username);
600     silc_free(client_entry->hostname);
601     silc_free(client_entry->server);
602     silc_hash_table_free(client_entry->channels);
603     silc_free(client_entry);
604     return NULL;
605   }
606
607   return client_entry;
608 }
609
610 /* Updates the `client_entry' with the new information sent as argument. */
611
612 void silc_client_update_client(SilcClient client,
613                                SilcClientConnection conn,
614                                SilcClientEntry client_entry,
615                                const char *nickname,
616                                const char *username,
617                                const char *userinfo,
618                                SilcUInt32 mode)
619 {
620   char *nick = NULL;
621
622   SILC_LOG_DEBUG(("Start"));
623
624   if (!client_entry->username && username)
625     silc_parse_userfqdn(username, &client_entry->username, 
626                         &client_entry->hostname);
627   if (!client_entry->realname && userinfo)
628     client_entry->realname = strdup(userinfo);
629   if (!client_entry->nickname && nickname) {
630     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
631     client_entry->nickname = strdup(nick);
632     silc_client_nickname_format(client, conn, client_entry);
633   }
634   client_entry->mode = mode;
635
636   if (nick) {
637     /* Remove the old cache entry and create a new one */
638     silc_idcache_del_by_context(conn->client_cache, client_entry);
639     silc_idcache_add(conn->client_cache, nick, client_entry->id, 
640                      client_entry, 0, NULL);
641   }
642 }
643
644 /* Deletes the client entry and frees all memory. */
645
646 void silc_client_del_client_entry(SilcClient client, 
647                                   SilcClientConnection conn,
648                                   SilcClientEntry client_entry)
649 {
650   SILC_LOG_DEBUG(("Start"));
651
652   silc_free(client_entry->nickname);
653   silc_free(client_entry->username);
654   silc_free(client_entry->realname);
655   silc_free(client_entry->hostname);
656   silc_free(client_entry->server);
657   silc_free(client_entry->id);
658   silc_free(client_entry->fingerprint);
659   silc_hash_table_free(client_entry->channels);
660   if (client_entry->send_key)
661     silc_cipher_free(client_entry->send_key);
662   if (client_entry->receive_key)
663     silc_cipher_free(client_entry->receive_key);
664   silc_free(client_entry->key);
665   silc_client_ftp_session_free_client(conn, client_entry);
666   if (client_entry->ke)
667     silc_client_abort_key_agreement(client, conn, client_entry);
668   silc_free(client_entry);
669 }
670
671 /* Removes client from the cache by the client entry. */
672
673 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
674                             SilcClientEntry client_entry)
675 {
676   bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
677
678   /* Remove from channels */
679   silc_client_remove_from_channels(client, conn, client_entry);
680
681   /* Free the client entry data */
682   silc_client_del_client_entry(client, conn, client_entry);
683
684   return ret;
685 }
686
687 /* Add new channel entry to the ID Cache */
688
689 SilcChannelEntry silc_client_add_channel(SilcClient client,
690                                          SilcClientConnection conn,
691                                          const char *channel_name,
692                                          SilcUInt32 mode, 
693                                          SilcChannelID *channel_id)
694 {
695   SilcChannelEntry channel;
696
697   SILC_LOG_DEBUG(("Start"));
698
699   channel = silc_calloc(1, sizeof(*channel));
700   channel->channel_name = strdup(channel_name);
701   channel->id = channel_id;
702   channel->mode = mode;
703   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
704                                              NULL, NULL, NULL, TRUE);
705
706   /* Put it to the ID cache */
707   if (!silc_idcache_add(conn->channel_cache, channel->channel_name, 
708                         (void *)channel->id, (void *)channel, 0, NULL)) {
709     silc_free(channel->channel_name);
710     silc_hash_table_free(channel->user_list);
711     silc_free(channel);
712     return NULL;
713   }
714
715   return channel;
716 }
717
718 /* Foreach callbcak to free all users from the channel when deleting a
719    channel entry. */
720
721 static void silc_client_del_channel_foreach(void *key, void *context,
722                                             void *user_context)
723 {
724   SilcChannelUser chu = (SilcChannelUser)context;
725
726   SILC_LOG_DEBUG(("Start"));
727
728   /* Remove the context from the client's channel hash table as that
729      table and channel's user_list hash table share this same context. */
730   silc_hash_table_del(chu->client->channels, chu->channel);
731   silc_free(chu);
732 }
733
734 /* Removes channel from the cache by the channel entry. */
735
736 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
737                              SilcChannelEntry channel)
738 {
739   bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
740
741   SILC_LOG_DEBUG(("Start"));
742
743   /* Free all client entrys from the users list. The silc_hash_table_free
744      will free all the entries so they are not freed at the foreach 
745      callback. */
746   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
747                           NULL);
748   silc_hash_table_free(channel->user_list);
749
750   silc_free(channel->channel_name);
751   silc_free(channel->id);
752   silc_free(channel->key);
753   if (channel->channel_key)
754     silc_cipher_free(channel->channel_key);
755   if (channel->hmac)
756     silc_hmac_free(channel->hmac);
757   if (channel->old_channel_key)
758     silc_cipher_free(channel->old_channel_key);
759   if (channel->old_hmac)
760     silc_hmac_free(channel->old_hmac);
761   if (channel->rekey_task)
762     silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
763   silc_client_del_channel_private_keys(client, conn, channel);
764   silc_free(channel);
765   return ret;
766 }
767
768 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
769    if the ID could not be changed. */
770
771 bool silc_client_replace_channel_id(SilcClient client,
772                                     SilcClientConnection conn,
773                                     SilcChannelEntry channel,
774                                     SilcChannelID *new_id)
775 {
776   if (!new_id)
777     return FALSE;
778
779   SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
780                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
781   SILC_LOG_DEBUG(("New Channel ID id(%s)", 
782                   silc_id_render(new_id, SILC_ID_CHANNEL)));
783
784   silc_idcache_del_by_id(conn->channel_cache, channel->id);
785   silc_free(channel->id);
786   channel->id = new_id;
787   return silc_idcache_add(conn->channel_cache, channel->channel_name, 
788                           (void *)channel->id, (void *)channel, 0, NULL);
789
790 }
791
792 /* Finds entry for channel by the channel name. Returns the entry or NULL
793    if the entry was not found. It is found only if the client is joined
794    to the channel. */
795
796 SilcChannelEntry silc_client_get_channel(SilcClient client,
797                                          SilcClientConnection conn,
798                                          char *channel)
799 {
800   SilcIDCacheEntry id_cache;
801   SilcChannelEntry entry;
802
803   SILC_LOG_DEBUG(("Start"));
804
805   if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
806                                      &id_cache))
807     return NULL;
808
809   entry = (SilcChannelEntry)id_cache->context;
810
811   SILC_LOG_DEBUG(("Found"));
812
813   return entry;
814 }
815
816 /* Finds entry for channel by the channel ID. Returns the entry or NULL
817    if the entry was not found. It is found only if the client is joined
818    to the channel. */
819
820 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
821                                                SilcClientConnection conn,
822                                                SilcChannelID *channel_id)
823 {
824   SilcIDCacheEntry id_cache;
825   SilcChannelEntry entry;
826
827   SILC_LOG_DEBUG(("Start"));
828
829   if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
830                                    &id_cache))
831     return NULL;
832
833   entry = (SilcChannelEntry)id_cache->context;
834
835   SILC_LOG_DEBUG(("Found"));
836
837   return entry;
838 }
839
840 typedef struct {
841   SilcClient client;
842   SilcClientConnection conn;
843   SilcChannelID *channel_id;
844   SilcGetChannelCallback completion;
845   void *context;
846 } *GetChannelByIDInternal;
847
848 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
849 {
850   GetChannelByIDInternal i = (GetChannelByIDInternal)context;
851   SilcChannelEntry entry;
852
853   SILC_LOG_DEBUG(("Start"));
854
855   /* Get the channel */
856   entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
857   if (entry) {
858     i->completion(i->client, i->conn, &entry, 1, i->context);
859   } else {
860     i->completion(i->client, i->conn, NULL, 0, i->context);
861   }
862
863   silc_free(i->channel_id);
864   silc_free(i);
865 }
866
867 /* Resolves channel information from the server by the channel ID. */
868
869 void silc_client_get_channel_by_id_resolve(SilcClient client,
870                                            SilcClientConnection conn,
871                                            SilcChannelID *channel_id,
872                                            SilcGetChannelCallback completion,
873                                            void *context)
874 {
875   SilcBuffer idp;
876   GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
877
878   SILC_LOG_DEBUG(("Start"));
879
880   i->client = client;
881   i->conn = conn;
882   i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
883   i->completion = completion;
884   i->context = context;
885       
886   /* Register our own command reply for this command */
887   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
888                                silc_client_command_reply_identify_i, 0,
889                                ++conn->cmd_ident);
890
891   /* Send the command */
892   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
893   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
894                            conn->cmd_ident,
895                            1, 5, idp->data, idp->len);
896   silc_buffer_free(idp);
897
898   /* Add pending callback */
899   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
900                               silc_client_command_get_channel_by_id_callback, 
901                               (void *)i);
902 }
903
904 /* Finds entry for server by the server name. */
905
906 SilcServerEntry silc_client_get_server(SilcClient client,
907                                        SilcClientConnection conn,
908                                        char *server_name)
909 {
910   SilcIDCacheEntry id_cache;
911   SilcServerEntry entry;
912
913   SILC_LOG_DEBUG(("Start"));
914
915   if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
916                                      &id_cache))
917     return NULL;
918
919   entry = (SilcServerEntry)id_cache->context;
920
921   return entry;
922 }
923
924 /* Finds entry for server by the server ID. */
925
926 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
927                                              SilcClientConnection conn,
928                                              SilcServerID *server_id)
929 {
930   SilcIDCacheEntry id_cache;
931   SilcServerEntry entry;
932
933   SILC_LOG_DEBUG(("Start"));
934
935   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
936                                    &id_cache))
937     return NULL;
938
939   entry = (SilcServerEntry)id_cache->context;
940
941   return entry;
942 }
943
944 /* Removes server from the cache by the server entry. */
945
946 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
947                             SilcServerEntry server)
948 {
949   bool ret = silc_idcache_del_by_context(conn->server_cache, server);
950   silc_free(server->server_name);
951   silc_free(server->server_info);
952   silc_free(server->server_id);
953   silc_free(server);
954   return ret;
955 }
956
957 /* Formats the nickname of the client specified by the `client_entry'.
958    If the format is specified by the application this will format the
959    nickname and replace the old nickname in the client entry. If the
960    format string is not specified then this function has no effect. */
961
962 void silc_client_nickname_format(SilcClient client, 
963                                  SilcClientConnection conn,
964                                  SilcClientEntry client_entry)
965 {
966   char *cp;
967   char *newnick = NULL;
968   int i, off = 0, len;
969   SilcClientEntry *clients;
970   SilcUInt32 clients_count = 0;
971   SilcClientEntry unformatted = NULL;
972
973   SILC_LOG_DEBUG(("Start"));
974
975   if (!client->internal->params->nickname_format[0])
976     return;
977
978   if (!client_entry->nickname)
979     return;
980
981   /* Get all clients with same nickname. Do not perform the formatting
982      if there aren't any clients with same nickname unless the application
983      is forcing us to do so. */
984   clients = silc_client_get_clients_local(client, conn,
985                                           client_entry->nickname, NULL,
986                                           &clients_count);
987   if (!clients && !client->internal->params->nickname_force_format)
988     return;
989
990   len = 0;
991   for (i = 0; i < clients_count; i++)
992     if (clients[i]->valid && clients[i] != client_entry)
993       len++;
994   if (!len)
995     return;
996
997   cp = client->internal->params->nickname_format;
998   while (*cp) {
999     if (*cp == '%') {
1000       cp++;
1001       continue;
1002     }
1003
1004     switch(*cp) {
1005     case 'n':
1006       /* Nickname */
1007       if (!client_entry->nickname)
1008         break;
1009       len = strlen(client_entry->nickname);
1010       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1011       memcpy(&newnick[off], client_entry->nickname, len);
1012       off += len;
1013       break;
1014     case 'h':
1015       /* Stripped hostname */
1016       if (!client_entry->hostname)
1017         break;
1018       len = strcspn(client_entry->hostname, ".");
1019       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1020       memcpy(&newnick[off], client_entry->hostname, len);
1021       off += len;
1022       break;
1023     case 'H':
1024       /* Full hostname */
1025       if (!client_entry->hostname)
1026         break;
1027       len = strlen(client_entry->hostname);
1028       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1029       memcpy(&newnick[off], client_entry->hostname, len);
1030       off += len;
1031       break;
1032     case 's':
1033       /* Stripped server name */
1034       if (!client_entry->server)
1035         break;
1036       len = strcspn(client_entry->server, ".");
1037       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1038       memcpy(&newnick[off], client_entry->server, len);
1039       off += len;
1040       break;
1041     case 'S':
1042       /* Full server name */
1043       if (!client_entry->server)
1044         break;
1045       len = strlen(client_entry->server);
1046       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1047       memcpy(&newnick[off], client_entry->server, len);
1048       off += len;
1049       break;
1050     case 'a':
1051       /* Ascending number */
1052       {
1053         char tmp[6];
1054         int num, max = 1;
1055
1056         if (clients_count == 1) {
1057           unformatted = clients[0];
1058           break;
1059         }
1060
1061         for (i = 0; i < clients_count; i++) {
1062           if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1063                            strlen(clients[i]->nickname)))
1064             unformatted = clients[i];
1065           if (strncasecmp(clients[i]->nickname, newnick, off))
1066             continue;
1067           if (strlen(clients[i]->nickname) <= off)
1068             continue;
1069           num = atoi(&clients[i]->nickname[off]);
1070           if (num > max)
1071             max = num;
1072         }
1073
1074         memset(tmp, 0, sizeof(tmp));
1075         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1076         len = strlen(tmp);
1077         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1078         memcpy(&newnick[off], tmp, len);
1079         off += len;
1080       }
1081       break;
1082     default:
1083       /* Some other character in the string */
1084       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1085       memcpy(&newnick[off], cp, 1);
1086       off++;
1087       break;
1088     }
1089
1090     cp++;
1091   }
1092
1093   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1094   newnick[off] = 0;
1095
1096   /* If we are changing nickname of our local entry we'll enforce
1097      that we will always get the unformatted nickname.  Give our
1098      format number to the one that is not formatted now. */
1099   if (unformatted && client_entry == conn->local_entry)
1100     client_entry = unformatted;
1101
1102   silc_free(client_entry->nickname);
1103   client_entry->nickname = newnick;
1104   silc_free(clients);
1105 }