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