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