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