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