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