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