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