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