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