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