Merged silc_1_0_branch to trunk.
[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_client_del_channel_private_keys(client, conn, channel);
987   silc_free(channel);
988   return ret;
989 }
990
991 /* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
992    if the ID could not be changed. */
993
994 bool silc_client_replace_channel_id(SilcClient client,
995                                     SilcClientConnection conn,
996                                     SilcChannelEntry channel,
997                                     SilcChannelID *new_id)
998 {
999   if (!new_id)
1000     return FALSE;
1001
1002   SILC_LOG_DEBUG(("Old Channel ID id(%s)",
1003                   silc_id_render(channel->id, SILC_ID_CHANNEL)));
1004   SILC_LOG_DEBUG(("New Channel ID id(%s)",
1005                   silc_id_render(new_id, SILC_ID_CHANNEL)));
1006
1007   silc_idcache_del_by_id(conn->internal->channel_cache, channel->id);
1008   silc_free(channel->id);
1009   channel->id = new_id;
1010   return silc_idcache_add(conn->internal->channel_cache,
1011                           channel->channel_name,
1012                           (void *)channel->id, (void *)channel, 0, NULL);
1013
1014 }
1015
1016 /* Finds entry for channel by the channel name. Returns the entry or NULL
1017    if the entry was not found. It is found only if the client is joined
1018    to the channel. */
1019
1020 SilcChannelEntry silc_client_get_channel(SilcClient client,
1021                                          SilcClientConnection conn,
1022                                          char *channel)
1023 {
1024   SilcIDCacheEntry id_cache;
1025   SilcChannelEntry entry;
1026
1027   assert(client && conn);
1028   if (!channel)
1029     return NULL;
1030
1031   SILC_LOG_DEBUG(("Start"));
1032
1033   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, channel,
1034                                      &id_cache))
1035     return NULL;
1036
1037   entry = (SilcChannelEntry)id_cache->context;
1038
1039   SILC_LOG_DEBUG(("Found"));
1040
1041   return entry;
1042 }
1043
1044 /* Finds entry for channel by the channel ID. Returns the entry or NULL
1045    if the entry was not found. It is found only if the client is joined
1046    to the channel. */
1047
1048 SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
1049                                                SilcClientConnection conn,
1050                                                SilcChannelID *channel_id)
1051 {
1052   SilcIDCacheEntry id_cache;
1053   SilcChannelEntry entry;
1054
1055   assert(client && conn);
1056   if (!channel_id)
1057     return NULL;
1058
1059   SILC_LOG_DEBUG(("Start"));
1060
1061   if (!silc_idcache_find_by_id_one(conn->internal->channel_cache, channel_id,
1062                                    &id_cache))
1063     return NULL;
1064
1065   entry = (SilcChannelEntry)id_cache->context;
1066
1067   SILC_LOG_DEBUG(("Found"));
1068
1069   return entry;
1070 }
1071
1072 typedef struct {
1073   SilcClient client;
1074   SilcClientConnection conn;
1075   union {
1076     SilcChannelID *channel_id;
1077     char *channel_name;
1078   } u;
1079   SilcGetChannelCallback completion;
1080   void *context;
1081 } *GetChannelInternal;
1082
1083 SILC_CLIENT_CMD_FUNC(get_channel_resolve_callback)
1084 {
1085   GetChannelInternal i = (GetChannelInternal)context;
1086   SilcChannelEntry entry;
1087
1088   SILC_LOG_DEBUG(("Start"));
1089
1090   /* Get the channel */
1091   entry = silc_client_get_channel(i->client, i->conn, i->u.channel_name);
1092   if (entry) {
1093     i->completion(i->client, i->conn, &entry, 1, i->context);
1094   } else {
1095     i->completion(i->client, i->conn, NULL, 0, i->context);
1096   }
1097
1098   silc_free(i->u.channel_name);
1099   silc_free(i);
1100 }
1101
1102 /* Resolves channel entry from the server by the channel name. */
1103
1104 void silc_client_get_channel_resolve(SilcClient client,
1105                                      SilcClientConnection conn,
1106                                      char *channel_name,
1107                                      SilcGetChannelCallback completion,
1108                                      void *context)
1109 {
1110   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1111
1112   assert(client && conn && channel_name);
1113
1114   SILC_LOG_DEBUG(("Start"));
1115
1116   i->client = client;
1117   i->conn = conn;
1118   i->u.channel_name = strdup(channel_name);
1119   i->completion = completion;
1120   i->context = context;
1121
1122   /* Register our own command reply for this command */
1123   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1124                                silc_client_command_reply_identify_i, 0,
1125                                ++conn->cmd_ident);
1126
1127   /* Send the command */
1128   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1129                            conn->cmd_ident,
1130                            1, 3, channel_name, strlen(channel_name));
1131
1132   /* Add pending callback */
1133   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1134                               silc_client_command_get_channel_resolve_callback,
1135                               (void *)i);
1136 }
1137
1138 SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
1139 {
1140   GetChannelInternal i = (GetChannelInternal)context;
1141   SilcChannelEntry entry;
1142
1143   SILC_LOG_DEBUG(("Start"));
1144
1145   /* Get the channel */
1146   entry = silc_client_get_channel_by_id(i->client, i->conn, i->u.channel_id);
1147   if (entry) {
1148     i->completion(i->client, i->conn, &entry, 1, i->context);
1149   } else {
1150     i->completion(i->client, i->conn, NULL, 0, i->context);
1151   }
1152
1153   silc_free(i->u.channel_id);
1154   silc_free(i);
1155 }
1156
1157 /* Resolves channel information from the server by the channel ID. */
1158
1159 void silc_client_get_channel_by_id_resolve(SilcClient client,
1160                                            SilcClientConnection conn,
1161                                            SilcChannelID *channel_id,
1162                                            SilcGetChannelCallback completion,
1163                                            void *context)
1164 {
1165   SilcBuffer idp;
1166   GetChannelInternal i = silc_calloc(1, sizeof(*i));
1167
1168   assert(client && conn && channel_id);
1169
1170   SILC_LOG_DEBUG(("Start"));
1171
1172   i->client = client;
1173   i->conn = conn;
1174   i->u.channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
1175   i->completion = completion;
1176   i->context = context;
1177
1178   /* Register our own command reply for this command */
1179   silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
1180                                silc_client_command_reply_identify_i, 0,
1181                                ++conn->cmd_ident);
1182
1183   /* Send the command */
1184   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1185   silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
1186                            conn->cmd_ident,
1187                            1, 5, idp->data, idp->len);
1188   silc_buffer_free(idp);
1189
1190   /* Add pending callback */
1191   silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1192                               silc_client_command_get_channel_by_id_callback,
1193                               (void *)i);
1194 }
1195
1196 /* Finds entry for server by the server name. */
1197
1198 SilcServerEntry silc_client_get_server(SilcClient client,
1199                                        SilcClientConnection conn,
1200                                        char *server_name)
1201 {
1202   SilcIDCacheEntry id_cache;
1203   SilcServerEntry entry;
1204
1205   assert(client && conn);
1206   if (!server_name)
1207     return NULL;
1208
1209   SILC_LOG_DEBUG(("Start"));
1210
1211   if (!silc_idcache_find_by_name_one(conn->internal->server_cache,
1212                                      server_name, &id_cache))
1213     return NULL;
1214
1215   entry = (SilcServerEntry)id_cache->context;
1216
1217   return entry;
1218 }
1219
1220 /* Finds entry for server by the server ID. */
1221
1222 SilcServerEntry silc_client_get_server_by_id(SilcClient client,
1223                                              SilcClientConnection conn,
1224                                              SilcServerID *server_id)
1225 {
1226   SilcIDCacheEntry id_cache;
1227   SilcServerEntry entry;
1228
1229   assert(client && conn);
1230   if (!server_id)
1231     return NULL;
1232
1233   SILC_LOG_DEBUG(("Start"));
1234
1235   if (!silc_idcache_find_by_id_one(conn->internal->server_cache,
1236                                    (void *)server_id, &id_cache))
1237     return NULL;
1238
1239   entry = (SilcServerEntry)id_cache->context;
1240
1241   return entry;
1242 }
1243
1244 /* Add new server entry */
1245
1246 SilcServerEntry silc_client_add_server(SilcClient client,
1247                                        SilcClientConnection conn,
1248                                        const char *server_name,
1249                                        const char *server_info,
1250                                        SilcServerID *server_id)
1251 {
1252   SilcServerEntry server_entry;
1253
1254   SILC_LOG_DEBUG(("Start"));
1255
1256   server_entry = silc_calloc(1, sizeof(*server_entry));
1257   if (!server_entry || !server_id)
1258     return NULL;
1259
1260   server_entry->server_id = server_id;
1261   if (server_name)
1262     server_entry->server_name = strdup(server_name);
1263   if (server_info)
1264     server_entry->server_info = strdup(server_info);
1265
1266   /* Add server to cache */
1267   if (!silc_idcache_add(conn->internal->server_cache,
1268                         server_entry->server_name,
1269                         server_entry->server_id, server_entry, 0, NULL)) {
1270     silc_free(server_entry->server_id);
1271     silc_free(server_entry->server_name);
1272     silc_free(server_entry->server_info);
1273     silc_free(server_entry);
1274     return NULL;
1275   }
1276
1277   return server_entry;
1278 }
1279
1280 /* Removes server from the cache by the server entry. */
1281
1282 bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
1283                             SilcServerEntry server)
1284 {
1285   bool ret = silc_idcache_del_by_context(conn->internal->server_cache, server);
1286   silc_free(server->server_name);
1287   silc_free(server->server_info);
1288   silc_free(server->server_id);
1289   silc_free(server);
1290   return ret;
1291 }
1292
1293 /* Updates the `server_entry' with the new information sent as argument. */
1294
1295 void silc_client_update_server(SilcClient client,
1296                                SilcClientConnection conn,
1297                                SilcServerEntry server_entry,
1298                                const char *server_name,
1299                                const char *server_info)
1300 {
1301   SILC_LOG_DEBUG(("Start"));
1302
1303   if (server_name && (!server_entry->server_name ||
1304                       strcmp(server_entry->server_name, server_name))) {
1305
1306     silc_idcache_del_by_context(conn->internal->server_cache, server_entry);
1307     silc_free(server_entry->server_name);
1308     server_entry->server_name = strdup(server_name);
1309     silc_idcache_add(conn->internal->server_cache, server_entry->server_name,
1310                      server_entry->server_id,
1311                      server_entry, 0, NULL);
1312   }
1313
1314   if (server_info && (!server_entry->server_info ||
1315                       strcmp(server_entry->server_info, server_info))) {
1316     silc_free(server_entry->server_info);
1317     server_entry->server_info = strdup(server_info);
1318   }
1319 }
1320
1321 /* Formats the nickname of the client specified by the `client_entry'.
1322    If the format is specified by the application this will format the
1323    nickname and replace the old nickname in the client entry. If the
1324    format string is not specified then this function has no effect. */
1325
1326 void silc_client_nickname_format(SilcClient client,
1327                                  SilcClientConnection conn,
1328                                  SilcClientEntry client_entry)
1329 {
1330   char *cp;
1331   char *newnick = NULL;
1332   int i, off = 0, len;
1333   bool freebase;
1334   SilcClientEntry *clients;
1335   SilcUInt32 clients_count = 0;
1336   SilcClientEntry unformatted = NULL;
1337
1338   SILC_LOG_DEBUG(("Start"));
1339
1340   if (!client->internal->params->nickname_format[0])
1341     return;
1342
1343   if (!client_entry->nickname)
1344     return;
1345
1346   /* Get all clients with same nickname. Do not perform the formatting
1347      if there aren't any clients with same nickname unless the application
1348      is forcing us to do so. */
1349   clients = silc_client_get_clients_local(client, conn,
1350                                           client_entry->nickname, NULL,
1351                                           &clients_count);
1352   if (!clients && !client->internal->params->nickname_force_format)
1353     return;
1354
1355   len = 0;
1356   freebase = TRUE;
1357   for (i = 0; i < clients_count; i++) {
1358     if (clients[i]->valid && clients[i] != client_entry)
1359       len++;
1360     if (clients[i]->valid && clients[i] != client_entry &&
1361         !strcmp(clients[i]->nickname, client_entry->nickname))
1362       freebase = FALSE;
1363   }
1364   if (!len || freebase)
1365     return;
1366
1367   if (clients_count == 1)
1368     unformatted = clients[0];
1369   else
1370     for (i = 0; i < clients_count; i++)
1371       if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
1372                        strlen(clients[i]->nickname)))
1373         unformatted = clients[i];
1374
1375   /* If we are changing nickname of our local entry we'll enforce
1376      that we will always get the unformatted nickname.  Give our
1377      format number to the one that is not formatted now. */
1378   if (unformatted && client_entry == conn->local_entry)
1379     client_entry = unformatted;
1380
1381   cp = client->internal->params->nickname_format;
1382   while (*cp) {
1383     if (*cp == '%') {
1384       cp++;
1385       continue;
1386     }
1387
1388     switch(*cp) {
1389     case 'n':
1390       /* Nickname */
1391       if (!client_entry->nickname)
1392         break;
1393       len = strlen(client_entry->nickname);
1394       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1395       memcpy(&newnick[off], client_entry->nickname, len);
1396       off += len;
1397       break;
1398     case 'h':
1399       /* Stripped hostname */
1400       if (!client_entry->hostname)
1401         break;
1402       len = strcspn(client_entry->hostname, ".");
1403       i = strcspn(client_entry->hostname, "-");
1404       if (i < len)
1405         len = i;
1406       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1407       memcpy(&newnick[off], client_entry->hostname, len);
1408       off += len;
1409       break;
1410     case 'H':
1411       /* Full hostname */
1412       if (!client_entry->hostname)
1413         break;
1414       len = strlen(client_entry->hostname);
1415       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1416       memcpy(&newnick[off], client_entry->hostname, len);
1417       off += len;
1418       break;
1419     case 's':
1420       /* Stripped server name */
1421       if (!client_entry->server)
1422         break;
1423       len = strcspn(client_entry->server, ".");
1424       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1425       memcpy(&newnick[off], client_entry->server, len);
1426       off += len;
1427       break;
1428     case 'S':
1429       /* Full server name */
1430       if (!client_entry->server)
1431         break;
1432       len = strlen(client_entry->server);
1433       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1434       memcpy(&newnick[off], client_entry->server, len);
1435       off += len;
1436       break;
1437     case 'a':
1438       /* Ascending number */
1439       {
1440         char tmp[6];
1441         int num, max = 1;
1442
1443         if (clients_count == 1)
1444           break;
1445
1446         for (i = 0; i < clients_count; i++) {
1447           if (strncasecmp(clients[i]->nickname, newnick, off))
1448             continue;
1449           if (strlen(clients[i]->nickname) <= off)
1450             continue;
1451           num = atoi(&clients[i]->nickname[off]);
1452           if (num > max)
1453             max = num;
1454         }
1455
1456         memset(tmp, 0, sizeof(tmp));
1457         snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
1458         len = strlen(tmp);
1459         newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
1460         memcpy(&newnick[off], tmp, len);
1461         off += len;
1462       }
1463       break;
1464     default:
1465       /* Some other character in the string */
1466       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1467       memcpy(&newnick[off], cp, 1);
1468       off++;
1469       break;
1470     }
1471
1472     cp++;
1473   }
1474
1475   newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
1476   newnick[off] = 0;
1477
1478   silc_free(client_entry->nickname);
1479   client_entry->nickname = newnick;
1480   silc_free(clients);
1481 }