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