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