07bead3afc3e3c1c430d89272dd87136738ce97d
[silc.git] / lib / silcclient / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2003 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 typedef struct {
291   SilcClient client;
292   SilcClientConnection conn;
293   SilcUInt32 list_count;
294   SilcBuffer client_id_list;
295   SilcGetClientCallback completion;
296   void *context;
297   int res_count;
298 } *GetClientsByListInternal;
299
300 SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
301 {
302   GetClientsByListInternal i = (GetClientsByListInternal)context;
303   SilcIDCacheEntry id_cache = NULL;
304   SilcBuffer client_id_list = i->client_id_list;
305   SilcClientEntry *clients = NULL;
306   SilcUInt32 clients_count = 0;
307   bool found = FALSE;
308   int c;
309
310   SILC_LOG_DEBUG(("Start"));
311
312   if (i->res_count) {
313     i->res_count--;
314     if (i->res_count)
315       return;
316   }
317
318   SILC_LOG_DEBUG(("Resolved all clients"));
319
320   clients = silc_calloc(i->list_count, sizeof(*clients));
321
322   for (c = 0; c < i->list_count; c++) {
323     SilcUInt16 idp_len;
324     SilcClientID *client_id;
325
326     /* Get Client ID */
327     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
328     idp_len += 4;
329     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
330     if (!client_id) {
331       silc_buffer_pull(client_id_list, idp_len);
332       continue;
333     }
334
335     /* Get the client entry */
336     if (silc_idcache_find_by_id_one_ext(i->conn->internal->client_cache,
337                                         (void *)client_id,
338                                         NULL, NULL,
339                                         silc_hash_client_id_compare, NULL,
340                                         &id_cache)) {
341       clients[clients_count] = (SilcClientEntry)id_cache->context;
342       clients_count++;
343       found = TRUE;
344     }
345
346     silc_free(client_id);
347     silc_buffer_pull(client_id_list, idp_len);
348   }
349
350   if (found) {
351     i->completion(i->client, i->conn, clients, clients_count, i->context);
352     silc_free(clients);
353   } else {
354     i->completion(i->client, i->conn, NULL, 0, i->context);
355   }
356
357   if (i->client_id_list)
358     silc_buffer_free(i->client_id_list);
359   silc_free(i);
360 }
361
362 /* Gets client entries by the list of client ID's `client_id_list'. This
363    always resolves those client ID's it does not know yet from the server
364    so this function might take a while. The `client_id_list' is a list
365    of ID Payloads added one after other.  JOIN command reply and USERS
366    command reply for example returns this sort of list. The `completion'
367    will be called after the entries are available. */
368
369 void silc_client_get_clients_by_list(SilcClient client,
370                                      SilcClientConnection conn,
371                                      SilcUInt32 list_count,
372                                      SilcBuffer client_id_list,
373                                      SilcGetClientCallback completion,
374                                      void *context)
375 {
376   SilcIDCacheEntry id_cache = NULL;
377   int i;
378   unsigned char **res_argv = NULL;
379   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
380   GetClientsByListInternal in;
381   bool wait_res = FALSE;
382
383   assert(client && conn && client_id_list);
384
385   SILC_LOG_DEBUG(("Start"));
386
387   in = silc_calloc(1, sizeof(*in));
388   in->client = client;
389   in->conn = conn;
390   in->list_count = list_count;
391   in->client_id_list = silc_buffer_copy(client_id_list);
392   in->completion = completion;
393   in->context = context;
394
395   for (i = 0; i < list_count; i++) {
396     SilcUInt16 idp_len;
397     SilcClientID *client_id;
398     SilcClientEntry entry;
399     bool ret;
400
401     /* Get Client ID */
402     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
403     idp_len += 4;
404     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
405     if (!client_id) {
406       silc_buffer_pull(client_id_list, idp_len);
407       continue;
408     }
409
410     /* Check if we have this client cached already. */
411     ret =
412       silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
413                                       (void *)client_id, NULL, NULL,
414                                       silc_hash_client_id_compare, NULL,
415                                       &id_cache);
416
417     /* If we don't have the entry or it has incomplete info, then resolve
418        it from the server. */
419     if (!ret || !((SilcClientEntry)id_cache->context)->nickname) {
420       entry = ret ? (SilcClientEntry)id_cache->context : NULL;
421
422       if (entry) {
423         if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
424           /* Attach to this resolving and wait until it finishes */
425           silc_client_command_pending(
426                             conn, SILC_COMMAND_NONE,
427                             entry->resolve_cmd_ident,
428                             silc_client_command_get_clients_list_callback,
429                             (void *)in);
430           wait_res = TRUE;
431           in->res_count++;
432
433           silc_free(client_id);
434           silc_buffer_pull(client_id_list, idp_len);
435           continue;
436         }
437
438         entry->status |= SILC_CLIENT_STATUS_RESOLVING;
439         entry->resolve_cmd_ident = conn->cmd_ident + 1;
440       }
441
442       /* No we don't have it, query it from the server. Assemble argument
443          table that will be sent for the IDENTIFY command later. */
444       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
445                               (res_argc + 1));
446       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
447                                    (res_argc + 1));
448       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
449                                     (res_argc + 1));
450       res_argv[res_argc] = client_id_list->data;
451       res_argv_lens[res_argc] = idp_len;
452       res_argv_types[res_argc] = res_argc + 5;
453       res_argc++;
454     }
455
456     silc_free(client_id);
457     silc_buffer_pull(client_id_list, idp_len);
458   }
459
460   silc_buffer_push(client_id_list, client_id_list->data -
461                    client_id_list->head);
462
463   /* Query the client information from server if the list included clients
464      that we don't know about. */
465   if (res_argc) {
466     SilcBuffer res_cmd;
467
468     /* Send the IDENTIFY command to server */
469     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
470                                           res_argc, res_argv, res_argv_lens,
471                                           res_argv_types, ++conn->cmd_ident);
472     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
473                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
474                             TRUE);
475
476     /* Register our own command reply for this command */
477     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
478                                  silc_client_command_reply_identify_i, 0,
479                                  conn->cmd_ident);
480
481     /* Process the applications request after reply has been received  */
482     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
483                                 silc_client_command_get_clients_list_callback,
484                                 (void *)in);
485     in->res_count++;
486
487     silc_buffer_free(res_cmd);
488     silc_free(res_argv);
489     silc_free(res_argv_lens);
490     silc_free(res_argv_types);
491     return;
492   }
493
494   if (wait_res)
495     return;
496
497   /* We have the clients in cache, get them and call the completion */
498   silc_client_command_get_clients_list_callback((void *)in, NULL);
499 }
500
501 typedef struct {
502   SilcClient client;
503   SilcClientConnection conn;
504   SilcChannelID channel_id;
505   SilcGetClientCallback completion;
506   void *context;
507   int res_count;
508 } *GetClientsByChannelInternal;
509
510 SILC_CLIENT_CMD_FUNC(get_clients_by_channel_cb)
511 {
512   GetClientsByChannelInternal i = context;
513   SilcClientEntry *clients = NULL;
514   SilcUInt32 clients_count = 0;
515   bool found = FALSE;
516   SilcChannelEntry channel;
517   SilcHashTableList htl;
518   SilcChannelUser chu;
519
520   channel = silc_client_get_channel_by_id(i->client, i->conn, &i->channel_id);
521   if (channel && !silc_hash_table_count(channel->user_list)) {
522     clients = silc_calloc(silc_hash_table_count(channel->user_list),
523                           sizeof(*clients));
524     silc_hash_table_list(channel->user_list, &htl);
525     while (silc_hash_table_get(&htl, NULL, (void *)&chu))
526       clients[clients_count++] = chu->client;
527     silc_hash_table_list_reset(&htl);
528     found = TRUE;
529   }
530
531   if (found) {
532     i->completion(i->client, i->conn, clients, clients_count, i->context);
533     silc_free(clients);
534   } else {
535     i->completion(i->client, i->conn, NULL, 0, i->context);
536   }
537
538   silc_free(i);
539 }
540
541 /* Gets client entries by the channel entry indicated by `channel'.  Thus,
542    it resolves the clients currently on that channel. */
543
544 void silc_client_get_clients_by_channel(SilcClient client,
545                                         SilcClientConnection conn,
546                                         SilcChannelEntry channel,
547                                         SilcGetClientCallback completion,
548                                         void *context)
549 {
550   GetClientsByChannelInternal in;
551   SilcHashTableList htl;
552   SilcChannelUser chu;
553   SilcClientEntry entry;
554   unsigned char **res_argv = NULL;
555   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
556   SilcBuffer idp;
557   bool wait_res = FALSE;
558
559   assert(client && conn && channel);
560
561   SILC_LOG_DEBUG(("Start"));
562
563   in = silc_calloc(1, sizeof(*in));
564   in->client = client;
565   in->conn = conn;
566   in->channel_id = *channel->id;
567   in->completion = completion;
568   in->context = context;
569
570   /* If user list does not exist, send USERS command. */
571   if (!channel->user_list || !silc_hash_table_count(channel->user_list)) {
572     SILC_LOG_DEBUG(("Sending USERS"));
573     silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
574                                  silc_client_command_reply_users_i, 0,
575                                  ++conn->cmd_ident);
576     silc_client_command_send(client, conn, SILC_COMMAND_USERS,
577                              conn->cmd_ident, 1, 2, channel->channel_name,
578                              strlen(channel->channel_name));
579     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
580                                 silc_client_command_get_clients_by_channel_cb,
581                                 in);
582     return;
583   }
584
585   silc_hash_table_list(channel->user_list, &htl);
586   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
587     entry = chu->client;
588
589     /* If the entry has incomplete info, then resolve it from the server. */
590     if (!entry->nickname || !entry->realname) {
591       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
592         /* Attach to this resolving and wait until it finishes */
593         silc_client_command_pending(
594                             conn, SILC_COMMAND_NONE,
595                             entry->resolve_cmd_ident,
596                             silc_client_command_get_clients_by_channel_cb,
597                             (void *)in);
598         wait_res = TRUE;
599         in->res_count++;
600         continue;
601       }
602       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
603       entry->resolve_cmd_ident = conn->cmd_ident + 1;
604
605       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
606
607       /* No we don't have it, query it from the server. Assemble argument
608          table that will be sent for the WHOIS command later. */
609       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
610                               (res_argc + 1));
611       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
612                                    (res_argc + 1));
613       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
614                                     (res_argc + 1));
615       res_argv[res_argc] = silc_memdup(idp->data, idp->len);
616       res_argv_lens[res_argc] = idp->len;
617       res_argv_types[res_argc] = res_argc + 4;
618       res_argc++;
619
620       silc_buffer_free(idp);
621     }
622   }
623   silc_hash_table_list_reset(&htl);
624
625   /* Query the client information from server if the list included clients
626      that we don't know about. */
627   if (res_argc) {
628     SilcBuffer res_cmd;
629
630     /* Send the WHOIS command to server */
631     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
632                                           res_argc, res_argv, res_argv_lens,
633                                           res_argv_types, ++conn->cmd_ident);
634     silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
635                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
636                             TRUE);
637
638     /* Register our own command reply for this command */
639     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
640                                  silc_client_command_reply_whois_i, 0,
641                                  conn->cmd_ident);
642
643     /* Process the applications request after reply has been received  */
644     silc_client_command_pending(
645                            conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
646                            silc_client_command_get_clients_by_channel_cb,
647                            (void *)in);
648     in->res_count++;
649
650     silc_buffer_free(res_cmd);
651     silc_free(res_argv);
652     silc_free(res_argv_lens);
653     silc_free(res_argv_types);
654     return;
655   }
656
657   if (wait_res)
658     return;
659
660   /* We have the clients in cache, get them and call the completion */
661   silc_client_command_get_clients_by_channel_cb((void *)in, NULL);
662 }
663
664 /* Finds entry for client by the client's ID. Returns the entry or NULL
665    if the entry was not found. */
666
667 SilcClientEntry silc_client_get_client_by_id(SilcClient client,
668                                              SilcClientConnection conn,
669                                              SilcClientID *client_id)
670 {
671   SilcIDCacheEntry id_cache;
672
673   assert(client && conn);
674   if (!client_id)
675     return NULL;
676
677   SILC_LOG_DEBUG(("Finding client by ID (%s)",
678                   silc_id_render(client_id, SILC_ID_CLIENT)));
679
680   /* Find ID from cache */
681   if (!silc_idcache_find_by_id_one_ext(conn->internal->client_cache,
682                                        (void *)client_id, NULL, NULL,
683                                        silc_hash_client_id_compare, NULL,
684                                        &id_cache))
685     return NULL;
686
687   SILC_LOG_DEBUG(("Found"));
688
689   return (SilcClientEntry)id_cache->context;
690 }
691
692 typedef struct {
693   SilcClient client;
694   SilcClientConnection conn;
695   SilcClientID *client_id;
696   SilcGetClientCallback completion;
697   void *context;
698 } *GetClientByIDInternal;
699
700 SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
701 {
702   GetClientByIDInternal i = (GetClientByIDInternal)context;
703   SilcClientEntry entry;
704
705   /* Get the client */
706   entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
707   if (entry) {
708     if (i->completion)
709       i->completion(i->client, i->conn, &entry, 1, i->context);
710   } else {
711     if (i->completion)
712       i->completion(i->client, i->conn, NULL, 0, i->context);
713   }
714
715   silc_free(i->client_id);
716   silc_free(i);
717 }
718
719 /* Same as above but will always resolve the information from the server.
720    Use this only if you know that you don't have the entry and the only
721    thing you know about the client is its ID. */
722
723 void silc_client_get_client_by_id_resolve(SilcClient client,
724                                           SilcClientConnection conn,
725                                           SilcClientID *client_id,
726                                           SilcBuffer attributes,
727                                           SilcGetClientCallback completion,
728                                           void *context)
729 {
730   SilcBuffer idp;
731   GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
732
733   assert(client && conn && client_id);
734
735   SILC_LOG_DEBUG(("Start"));
736
737   i->client = client;
738   i->conn = conn;
739   i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
740   i->completion = completion;
741   i->context = context;
742
743   /* Register our own command reply for this command */
744   silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
745                                silc_client_command_reply_whois_i, 0,
746                                ++conn->cmd_ident);
747
748   /* Send the command */
749   idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
750   silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
751                            2, 3, attributes ? attributes->data : NULL,
752                            attributes ? attributes->len : 0,
753                            4, idp->data, idp->len);
754   silc_buffer_free(idp);
755
756   /* Add pending callback */
757   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
758                               silc_client_command_get_client_by_id_callback,
759                               (void *)i);
760 }
761
762
763 /******************************************************************************
764
765                 Client, Channel and Server entry manipulation
766
767 ******************************************************************************/
768
769
770 /* Creates new client entry and adds it to the ID cache. Returns pointer
771    to the new entry. */
772
773 SilcClientEntry
774 silc_client_add_client(SilcClient client, SilcClientConnection conn,
775                        char *nickname, char *username,
776                        char *userinfo, SilcClientID *id, SilcUInt32 mode)
777 {
778   SilcClientEntry client_entry;
779   char *nick = NULL;
780
781   SILC_LOG_DEBUG(("Start"));
782
783   /* Save the client infos */
784   client_entry = silc_calloc(1, sizeof(*client_entry));
785   client_entry->id = id;
786   client_entry->valid = TRUE;
787   silc_parse_userfqdn(nickname, &nick, &client_entry->server);
788   silc_parse_userfqdn(username, &client_entry->username,
789                       &client_entry->hostname);
790   if (userinfo)
791     client_entry->realname = strdup(userinfo);
792   client_entry->mode = mode;
793   if (nick)
794     client_entry->nickname = strdup(nick);
795   client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
796                                                  NULL, NULL, NULL, TRUE);
797
798   /* Format the nickname */
799   silc_client_nickname_format(client, conn, client_entry);
800
801   /* Add client to cache, the non-formatted nickname is saved to cache */
802   if (!silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
803                         (void *)client_entry, 0, NULL)) {
804     silc_free(client_entry->nickname);
805     silc_free(client_entry->username);
806     silc_free(client_entry->hostname);
807     silc_free(client_entry->server);
808     silc_hash_table_free(client_entry->channels);
809     silc_free(client_entry);
810     return NULL;
811   }
812
813   return client_entry;
814 }
815
816 /* Updates the `client_entry' with the new information sent as argument. */
817
818 void silc_client_update_client(SilcClient client,
819                                SilcClientConnection conn,
820                                SilcClientEntry client_entry,
821                                const char *nickname,
822                                const char *username,
823                                const char *userinfo,
824                                SilcUInt32 mode)
825 {
826   char *nick = NULL;
827
828   SILC_LOG_DEBUG(("Start"));
829
830   if ((!client_entry->username || !client_entry->hostname) && username) {
831     silc_free(client_entry->username);
832     silc_free(client_entry->hostname);
833     client_entry->username = NULL;
834     client_entry->hostname = NULL;
835     silc_parse_userfqdn(username, &client_entry->username,
836                         &client_entry->hostname);
837   }
838   if (!client_entry->realname && userinfo)
839     client_entry->realname = strdup(userinfo);
840   if (!client_entry->nickname && nickname) {
841     silc_parse_userfqdn(nickname, &nick, &client_entry->server);
842     client_entry->nickname = strdup(nick);
843     silc_client_nickname_format(client, conn, client_entry);
844   }
845   client_entry->mode = mode;
846
847   if (nick) {
848     /* Remove the old cache entry and create a new one */
849     silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
850     silc_idcache_add(conn->internal->client_cache, nick, client_entry->id,
851                      client_entry, 0, NULL);
852   }
853 }
854
855 /* Deletes the client entry and frees all memory. */
856
857 void silc_client_del_client_entry(SilcClient client,
858                                   SilcClientConnection conn,
859                                   SilcClientEntry client_entry)
860 {
861   SILC_LOG_DEBUG(("Start"));
862
863   silc_free(client_entry->nickname);
864   silc_free(client_entry->username);
865   silc_free(client_entry->realname);
866   silc_free(client_entry->hostname);
867   silc_free(client_entry->server);
868   silc_free(client_entry->id);
869   silc_free(client_entry->fingerprint);
870   silc_hash_table_free(client_entry->channels);
871   if (client_entry->send_key)
872     silc_cipher_free(client_entry->send_key);
873   if (client_entry->receive_key)
874     silc_cipher_free(client_entry->receive_key);
875   silc_free(client_entry->key);
876   silc_client_ftp_session_free_client(conn, client_entry);
877   if (client_entry->ke)
878     silc_client_abort_key_agreement(client, conn, client_entry);
879   silc_free(client_entry);
880 }
881
882 /* Removes client from the cache by the client entry. */
883
884 bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
885                             SilcClientEntry client_entry)
886 {
887   bool ret = silc_idcache_del_by_context(conn->internal->client_cache,
888                                          client_entry);
889
890   if (ret) {
891     /* Remove from channels */
892     silc_client_remove_from_channels(client, conn, client_entry);
893
894     /* Free the client entry data */
895     silc_client_del_client_entry(client, conn, client_entry);
896   }
897
898   return ret;
899 }
900
901 /* Add new channel entry to the ID Cache */
902
903 SilcChannelEntry silc_client_add_channel(SilcClient client,
904                                          SilcClientConnection conn,
905                                          const char *channel_name,
906                                          SilcUInt32 mode,
907                                          SilcChannelID *channel_id)
908 {
909   SilcChannelEntry channel;
910
911   SILC_LOG_DEBUG(("Start"));
912
913   channel = silc_calloc(1, sizeof(*channel));
914   channel->channel_name = strdup(channel_name);
915   channel->id = channel_id;
916   channel->mode = mode;
917   channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
918                                              NULL, NULL, NULL, TRUE);
919
920   /* Put it to the ID cache */
921   if (!silc_idcache_add(conn->internal->channel_cache, channel->channel_name,
922                         (void *)channel->id, (void *)channel, 0, NULL)) {
923     silc_free(channel->channel_name);
924     silc_hash_table_free(channel->user_list);
925     silc_free(channel);
926     return NULL;
927   }
928
929   return channel;
930 }
931
932 /* Foreach callbcak to free all users from the channel when deleting a
933    channel entry. */
934
935 static void silc_client_del_channel_foreach(void *key, void *context,
936                                             void *user_context)
937 {
938   SilcChannelUser chu = (SilcChannelUser)context;
939
940   SILC_LOG_DEBUG(("Start"));
941
942   /* Remove the context from the client's channel hash table as that
943      table and channel's user_list hash table share this same context. */
944   silc_hash_table_del(chu->client->channels, chu->channel);
945   silc_free(chu);
946 }
947
948 /* Removes channel from the cache by the channel entry. */
949
950 bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
951                              SilcChannelEntry channel)
952 {
953   bool ret = silc_idcache_del_by_context(conn->internal->channel_cache,
954                                          channel);
955
956   SILC_LOG_DEBUG(("Start"));
957
958   /* Free all client entrys from the users list. The silc_hash_table_free
959      will free all the entries so they are not freed at the foreach
960      callback. */
961   silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
962                           NULL);
963   silc_hash_table_free(channel->user_list);
964
965   silc_free(channel->channel_name);
966   silc_free(channel->id);
967   silc_free(channel->key);
968   if (channel->channel_key)
969     silc_cipher_free(channel->channel_key);
970   if (channel->hmac)
971     silc_hmac_free(channel->hmac);
972   if (channel->old_channel_keys) {
973     SilcCipher key;
974     silc_dlist_start(channel->old_channel_keys);
975     while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END)
976       silc_cipher_free(key);
977     silc_dlist_uninit(channel->old_channel_keys);
978   }
979   if (channel->old_hmacs) {
980     SilcHmac hmac;
981     silc_dlist_start(channel->old_hmacs);
982     while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END)
983       silc_hmac_free(hmac);
984     silc_dlist_uninit(channel->old_hmacs);
985   }
986   silc_schedule_task_del_by_context(conn->client->schedule, channel);
987   silc_client_del_channel_private_keys(client, conn, channel);
988   silc_free(channel);
989   return ret;
990 }
991
992 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
993    if the ID could not be changed. */
994
995 bool silc_client_replace_channel_id(SilcClient client,
996                                     SilcClientConnection conn,
997                                     SilcChannelEntry channel,
998                                     SilcChannelID *new_id)
999 {
1000   if (!new_id)
1001     return FALSE;
1002
1003   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1004                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
1005   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1006                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1007
1008   silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1009   silc_free(channel->id);
1010   channel->id = new_id;
1011   return silc_idcache_add(conn->internal->channel_cache,
1012                           channel->channel_name,
1013                           (void *)channel->id, (void *)channel, 0, NULL);
1014
1015 }
1016
1017 /* Finds entry for channel by the channel name. Returns the entry or NULL
1018    if the entry was not found. It is found only if the client is joined
1019    to the channel. */
1020
1021 SilcChannelEntry silc_client_get_channel(SilcClient client,
1022                                          SilcClientConnection conn,
1023                                          char *channel)
1024 {
1025   SilcIDCacheEntry id_cache;
1026   SilcChannelEntry entry;
1027
1028   assert(client && conn);
1029   if (!channel)
1030     return NULL;
1031
1032   SILC_LOG_DEBUG(("Start"));
1033
1034   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1035                                      &id_cache))
1036     return NULL;
1037
1038   entry = (SilcChannelEntry)id_cache->context;
1039
1040   SILC_LOG_DEBUG(("Found"));
1041
1042   return entry;
1043 }
1044
1045 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1046    if the entry was not found. It is found only if the client is joined
1047    to the channel. */
1048
1049 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1050                                                SilcClientConnection conn,
1051                                                SilcChannelID *channel_id)
1052 {
1053   SilcIDCacheEntry id_cache;
1054   SilcChannelEntry entry;
1055
1056   assert(client && conn);
1057   if (!channel_id)
1058     return NULL;
1059
1060   SILC_LOG_DEBUG(("Start"));
1061
1062   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1063                                    &id_cache))
1064     return NULL;
1065
1066   entry = (SilcChannelEntry)id_cache->context;
1067
1068   SILC_LOG_DEBUG(("Found"));
1069
1070   return entry;
1071 }
1072
1073 typedef struct {
1074   SilcClient client;
1075   SilcClientConnection conn;
1076   union {
1077     SilcChannelID *channel_id;
1078     char *channel_name;
1079   } u;
1080   SilcGetChannelCallback completion;
1081   void *context;
1082 } *GetChannelInternal;
1083
1084 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1085 {
1086   GetChannelInternal i = (GetChannelInternal)context;
1087   SilcChannelEntry entry;
1088
1089   SILC_LOG_DEBUG(("Start"));
1090
1091   /* Get the channel */
1092   entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1093   if (entry) {
1094     i->completion(i->client, i->conn, &entry, 1, i->context);
1095   } else {
1096     i->completion(i->client, i->conn, NULL, 0, i->context);
1097   }
1098
1099   silc_free(i->u.channel_name);
1100   silc_free(i);
1101 }
1102
1103 /* Resolves channel entry from the server by the channel name. */
1104
1105 void silc_client_get_channel_resolve(SilcClient client,
1106                                      SilcClientConnection conn,
1107                                      char *channel_name,
1108                                      SilcGetChannelCallback completion,
1109                                      void *context)
1110 {
1111   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1112
1113   assert(client && conn && channel_name);
1114
1115   SILC_LOG_DEBUG(("Start"));
1116
1117   i->client = client;
1118   i->conn = conn;
1119   i->u.channel_name = strdup(channel_name);
1120   i->completion = completion;
1121   i->context = context;
1122
1123   /* Register our own command reply for this command */
1124   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1125                                silc_client_command_reply_identify_i, 0,
1126                                ++conn->cmd_ident);
1127
1128   /* Send the command */
1129   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1130                            conn->cmd_ident,
1131                            1, 3, channel_name, strlen(channel_name));
1132
1133   /* Add pending callback */
1134   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1135                               silc_client_command_get_channel_resolve_callback,
1136                               (void *)i);
1137 }
1138
1139 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1140 {
1141   GetChannelInternal i = (GetChannelInternal)context;
1142   SilcChannelEntry entry;
1143
1144   SILC_LOG_DEBUG(("Start"));
1145
1146   /* Get the channel */
1147   entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1148   if (entry) {
1149     i->completion(i->client, i->conn, &entry, 1, i->context);
1150   } else {
1151     i->completion(i->client, i->conn, NULL, 0, i->context);
1152   }
1153
1154   silc_free(i->u.channel_id);
1155   silc_free(i);
1156 }
1157
1158 /* Resolves channel information from the server by the channel ID. */
1159
1160 void silc_client_get_channel_by_id_resolve(SilcClient client,
1161                                            SilcClientConnection conn,
1162                                            SilcChannelID *channel_id,
1163                                            SilcGetChannelCallback completion,
1164                                            void *context)
1165 {
1166   SilcBuffer idp;
1167   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1168
1169   assert(client && conn && channel_id);
1170
1171   SILC_LOG_DEBUG(("Start"));
1172
1173   i->client = client;
1174   i->conn = conn;
1175   i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1176   i->completion = completion;
1177   i->context = context;
1178
1179   /* Register our own command reply for this command */
1180   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1181                                silc_client_command_reply_identify_i, 0,
1182                                ++conn->cmd_ident);
1183
1184   /* Send the command */
1185   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1186   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1187                            conn->cmd_ident,
1188                            1, 5, idp->data, idp->len);
1189   silc_buffer_free(idp);
1190
1191   /* Add pending callback */
1192   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1193                               silc_client_command_get_channel_by_id_callback,
1194                               (void *)i);
1195 }
1196
1197 /* Finds entry for server by the server name. */
1198
1199 SilcServerEntry silc_client_get_server(SilcClient client,
1200                                        SilcClientConnection conn,
1201                                        char *server_name)
1202 {
1203   SilcIDCacheEntry id_cache;
1204   SilcServerEntry entry;
1205
1206   assert(client && conn);
1207   if (!server_name)
1208     return NULL;
1209
1210   SILC_LOG_DEBUG(("Start"));
1211
1212   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1213                                      server_name, &id_cache))
1214     return NULL;
1215
1216   entry = (SilcServerEntry)id_cache->context;
1217
1218   return entry;
1219 }
1220
1221 /* Finds entry for server by the server ID. */
1222
1223 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1224                                              SilcClientConnection conn,
1225                                              SilcServerID *server_id)
1226 {
1227   SilcIDCacheEntry id_cache;
1228   SilcServerEntry entry;
1229
1230   assert(client && conn);
1231   if (!server_id)
1232     return NULL;
1233
1234   SILC_LOG_DEBUG(("Start"));
1235
1236   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1237                                    (void *)server_id, &id_cache))
1238     return NULL;
1239
1240   entry = (SilcServerEntry)id_cache->context;
1241
1242   return entry;
1243 }
1244
1245 /* Add new server entry */
1246
1247 SilcServerEntry silc_client_add_server(SilcClient client,
1248                                        SilcClientConnection conn,
1249                                        const char *server_name,
1250                                        const char *server_info,
1251                                        SilcServerID *server_id)
1252 {
1253   SilcServerEntry server_entry;
1254
1255   SILC_LOG_DEBUG(("Start"));
1256
1257   server_entry = silc_calloc(1, sizeof(*server_entry));
1258   if (!server_entry || !server_id)
1259     return NULL;
1260
1261   server_entry->server_id = server_id;
1262   if (server_name)
1263     server_entry->server_name = strdup(server_name);
1264   if (server_info)
1265     server_entry->server_info = strdup(server_info);
1266
1267   /* Add server to cache */
1268   if (!silc_idcache_add(conn->internal->server_cache,
1269                         server_entry->server_name,
1270                         server_entry->server_id, server_entry, 0, NULL)) {
1271     silc_free(server_entry->server_id);
1272     silc_free(server_entry->server_name);
1273     silc_free(server_entry->server_info);
1274     silc_free(server_entry);
1275     return NULL;
1276   }
1277
1278   return server_entry;
1279 }
1280
1281 /* Removes server from the cache by the server entry. */
1282
1283 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1284                             SilcServerEntry server)
1285 {
1286   bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1287   silc_free(server->server_name);
1288   silc_free(server->server_info);
1289   silc_free(server->server_id);
1290   silc_free(server);
1291   return ret;
1292 }
1293
1294 /* Updates the `server_entry' with the new information sent as argument. */
1295
1296 void silc_client_update_server(SilcClient client,
1297                                SilcClientConnection conn,
1298                                SilcServerEntry server_entry,
1299                                const char *server_name,
1300                                const char *server_info)
1301 {
1302   SILC_LOG_DEBUG(("Start"));
1303
1304   if (server_name && (!server_entry->server_name ||
1305                       strcmp(server_entry->server_name, server_name))) {
1306
1307     silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1308     silc_free(server_entry->server_name);
1309     server_entry->server_name = strdup(server_name);
1310     silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1311                      server_entry->server_id,
1312                      server_entry, 0, NULL);
1313   }
1314
1315   if (server_info && (!server_entry->server_info ||
1316                       strcmp(server_entry->server_info, server_info))) {
1317     silc_free(server_entry->server_info);
1318     server_entry->server_info = strdup(server_info);
1319   }
1320 }
1321
1322 /* Formats the nickname of the client specified by the `client_entry'.
1323    If the format is specified by the application this will format the
1324    nickname and replace the old nickname in the client entry. If the
1325    format string is not specified then this function has no effect. */
1326
1327 void silc_client_nickname_format(SilcClient client,
1328                                  SilcClientConnection conn,
1329                                  SilcClientEntry client_entry)
1330 {
1331   char *cp;
1332   char *newnick = NULL;
1333   int i, off = 0, len;
1334   bool freebase;
1335   SilcClientEntry *clients;
1336   SilcUInt32 clients_count = 0;
1337   SilcClientEntry unformatted = NULL;
1338
1339   SILC_LOG_DEBUG(("Start"));
1340
1341   if (!client->internal->params->nickname_format[0])
1342     return;
1343
1344   if (!client_entry->nickname)
1345     return;
1346
1347   /* Get all clients with same nickname. Do not perform the formatting
1348      if there aren't any clients with same nickname unless the application
1349      is forcing us to do so. */
1350   clients = silc_client_get_clients_local(client, conn,
1351                                           client_entry->nickname, NULL,
1352                                           &clients_count);
1353   if (!clients && !client->internal->params->nickname_force_format)
1354     return;
1355
1356   len = 0;
1357   freebase = TRUE;
1358   for (i = 0; i < clients_count; i++) {
1359     if (clients[i]->valid && clients[i] != client_entry)
1360       len++;
1361     if (clients[i]->valid && clients[i] != client_entry &&
1362         !strcmp(clients[i]->nickname, client_entry->nickname))
1363       freebase = FALSE;
1364   }
1365   if (!len || freebase)
1366     return;
1367
1368   if (clients_count == 1)
1369     unformatted = clients[0];
1370   else
1371     for (i = 0; i < clients_count; i++)
1372       if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1373                        strlen(clients[i]->nickname)))
1374         unformatted = clients[i];
1375
1376   /* If we are changing nickname of our local entry we'll enforce
1377      that we will always get the unformatted nickname.  Give our
1378      format number to the one that is not formatted now. */
1379   if (unformatted && client_entry == conn->local_entry)
1380     client_entry = unformatted;
1381
1382   cp = client->internal->params->nickname_format;
1383   while (*cp) {
1384     if (*cp == '%') {
1385       cp++;
1386       continue;
1387     }
1388
1389     switch(*cp) {
1390     case 'n':
1391       /* Nickname */
1392       if (!client_entry->nickname)
1393         break;
1394       len = strlen(client_entry->nickname);
1395       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1396       memcpy(&newnick[off], client_entry->nickname, len);
1397       off += len;
1398       break;
1399     case 'h':
1400       /* Stripped hostname */
1401       if (!client_entry->hostname)
1402         break;
1403       len = strcspn(client_entry->hostname, ".");
1404       i = strcspn(client_entry->hostname, "-");
1405       if (i < len)
1406         len = i;
1407       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1408       memcpy(&newnick[off], client_entry->hostname, len);
1409       off += len;
1410       break;
1411     case 'H':
1412       /* Full hostname */
1413       if (!client_entry->hostname)
1414         break;
1415       len = strlen(client_entry->hostname);
1416       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1417       memcpy(&newnick[off], client_entry->hostname, len);
1418       off += len;
1419       break;
1420     case 's':
1421       /* Stripped server name */
1422       if (!client_entry->server)
1423         break;
1424       len = strcspn(client_entry->server, ".");
1425       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1426       memcpy(&newnick[off], client_entry->server, len);
1427       off += len;
1428       break;
1429     case 'S':
1430       /* Full server name */
1431       if (!client_entry->server)
1432         break;
1433       len = strlen(client_entry->server);
1434       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1435       memcpy(&newnick[off], client_entry->server, len);
1436       off += len;
1437       break;
1438     case 'a':
1439       /* Ascending number */
1440       {
1441         char tmp[6];
1442         int num, max = 1;
1443
1444         if (clients_count == 1)
1445           break;
1446
1447         for (i = 0; i < clients_count; i++) {
1448           if (strncasecmp(clients[i]->nickname, newnick, off))
1449             continue;
1450           if (strlen(clients[i]->nickname) <= off)
1451             continue;
1452           num = atoi(&clients[i]->nickname[off]);
1453           if (num > max)
1454             max = num;
1455         }
1456
1457         memset(tmp, 0, sizeof(tmp));
1458         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1459         len = strlen(tmp);
1460         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1461         memcpy(&newnick[off], tmp, len);
1462         off += len;
1463       }
1464       break;
1465     default:
1466       /* Some other character in the string */
1467       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1468       memcpy(&newnick[off], cp, 1);
1469       off++;
1470       break;
1471     }
1472
1473     cp++;
1474   }
1475
1476   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1477   newnick[off] = 0;
1478
1479   silc_free(client_entry->nickname);
1480   client_entry->nickname = newnick;
1481   silc_free(clients);
1482 }