Fixed memory leak in WHOIS command reply.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34
35 #include "silcincludes.h"
36 #include "silcclient.h"
37 #include "client_internal.h"
38
39 #define SAY cmd->client->internal->ops->say
40
41 /* All functions that call the COMMAND_CHECK_STATUS macro must have 
42    out: and err: goto labels. out label should call the pending
43    command replies, and the err label just handle error condition. */
44
45 #define COMMAND_CHECK_STATUS                                    \
46 do {                                                            \
47   SILC_LOG_DEBUG(("Start"));                                    \
48   if (!silc_command_get_status(cmd->payload, NULL, NULL)) {     \
49     if (SILC_STATUS_IS_ERROR(cmd->status)) {                    \
50       /* Single error */                                        \
51       COMMAND_REPLY_ERROR;                                      \
52       goto out;                                                 \
53     }                                                           \
54     /* List of errors */                                        \
55     COMMAND_REPLY_ERROR;                                        \
56     if (cmd->status == SILC_STATUS_LIST_END)                    \
57       goto out;                                                 \
58     goto err;                                                   \
59   }                                                             \
60 } while(0)
61
62 /* Same as COMMAND_CHECK_STATUS but doesn't call client operation */
63 #define COMMAND_CHECK_STATUS_I                                  \
64 do {                                                            \
65   SILC_LOG_DEBUG(("Start"));                                    \
66   if (!silc_command_get_status(cmd->payload, NULL, NULL)) {     \
67     if (SILC_STATUS_IS_ERROR(cmd->status))                      \
68       goto out;                                                 \
69     if (cmd->status == SILC_STATUS_LIST_END)                    \
70       goto out;                                                 \
71     goto err;                                                   \
72   }                                                             \
73 } while(0)
74
75 /* Process received command reply. */
76
77 void silc_client_command_reply_process(SilcClient client,
78                                        SilcSocketConnection sock,
79                                        SilcPacketContext *packet)
80 {
81   SilcBuffer buffer = packet->buffer;
82   SilcClientCommand cmd;
83   SilcClientCommandReplyContext ctx;
84   SilcCommandPayload payload;
85   SilcCommand command;
86   SilcCommandCb reply = NULL;
87   
88   /* Get command reply payload from packet */
89   payload = silc_command_payload_parse(buffer->data, buffer->len);
90   if (!payload) {
91     /* Silently ignore bad reply packet */
92     SILC_LOG_DEBUG(("Bad command reply packet"));
93     return;
94   }
95   
96   /* Allocate command reply context. This must be free'd by the
97      command reply routine receiving it. */
98   ctx = silc_calloc(1, sizeof(*ctx));
99   ctx->users++;
100   ctx->client = client;
101   ctx->sock = sock;
102   ctx->payload = payload;
103   ctx->args = silc_command_get_args(ctx->payload);
104   ctx->packet = packet;
105   ctx->ident = silc_command_get_ident(ctx->payload);
106   silc_command_get_status(ctx->payload, &ctx->status, &ctx->error);
107
108   /* Check for pending commands and mark to be exeucted */
109   ctx->callbacks =
110     silc_client_command_pending_check(sock->user_data, ctx, 
111                                       silc_command_get(ctx->payload), 
112                                       ctx->ident, &ctx->callbacks_count);
113
114   /* Execute command reply */
115
116   command = silc_command_get(ctx->payload);
117
118   /* Try to find matching the command identifier */
119   silc_list_start(client->internal->commands);
120   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
121     if (cmd->cmd == command && !cmd->ident)
122       reply = cmd->reply;
123     if (cmd->cmd == command && cmd->ident == ctx->ident) {
124       (*cmd->reply)((void *)ctx, NULL);
125       break;
126     }
127   }
128
129   if (cmd == SILC_LIST_END) {
130     if (reply)
131       /* No specific identifier for command reply, call first one found */
132       (*reply)(ctx, NULL);
133     else
134       silc_free(ctx);
135   }
136 }
137
138 /* Duplicate Command Reply Context by adding reference counter. The context
139    won't be free'd untill it hits zero. */
140
141 SilcClientCommandReplyContext 
142 silc_client_command_reply_dup(SilcClientCommandReplyContext cmd)
143 {
144   cmd->users++;
145   SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, 
146                   cmd->users - 1, cmd->users));
147   return cmd;
148 }
149
150 /* Free command reply context and its internals. */
151
152 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
153 {
154   cmd->users--;
155   SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, 
156                   cmd->users + 1, cmd->users));
157   if (cmd->users < 1) {
158     silc_command_payload_free(cmd->payload);
159     silc_free(cmd);
160   }
161 }
162
163 static void 
164 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
165                                      SilcStatus status,
166                                      bool notify)
167 {
168   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
169   SilcClientID *client_id;
170   SilcClientEntry client_entry = NULL;
171   SilcUInt32 len;
172   unsigned char *id_data, *tmp;
173   char *nickname = NULL, *username = NULL;
174   char *realname = NULL;
175   SilcUInt32 idle = 0, mode = 0;
176   SilcBufferStruct channels, ch_user_modes;
177   bool has_channels = FALSE, has_user_modes = FALSE;
178   unsigned char *fingerprint;
179   SilcUInt32 fingerprint_len;
180   
181   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
182   if (!id_data) {
183     if (notify)
184       COMMAND_REPLY_ERROR;
185     return;
186   }
187   
188   client_id = silc_id_payload_parse_id(id_data, len, NULL);
189   if (!client_id) {
190     if (notify)
191       COMMAND_REPLY_ERROR;
192     return;
193   }
194   
195   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
196   username = silc_argument_get_arg_type(cmd->args, 4, &len);
197   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
198   if (!nickname || !username || !realname) {
199     if (notify)
200       COMMAND_REPLY_ERROR;
201     return;
202   }
203
204   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
205   if (tmp) {
206     silc_buffer_set(&channels, tmp, len);
207     has_channels = TRUE;
208   }
209
210   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
211   if (tmp)
212     SILC_GET32_MSB(mode, tmp);
213
214   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
215   if (tmp)
216     SILC_GET32_MSB(idle, tmp);
217
218   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
219
220   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
221   if (tmp) {
222     silc_buffer_set(&ch_user_modes, tmp, len);
223     has_user_modes = TRUE;
224   }
225
226   /* Check if we have this client cached already. */
227   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
228   if (!client_entry) {
229     SILC_LOG_DEBUG(("Adding new client entry"));
230     client_entry = 
231       silc_client_add_client(cmd->client, conn, nickname, username, realname,
232                              client_id, mode);
233   } else {
234     silc_client_update_client(cmd->client, conn, client_entry, 
235                               nickname, username, realname, mode);
236     silc_free(client_id);
237   }
238
239   if (fingerprint && !client_entry->fingerprint) {
240     client_entry->fingerprint = silc_memdup(fingerprint, fingerprint_len);
241     client_entry->fingerprint_len = fingerprint_len;
242   }
243
244   /* Take Requested Attributes if set. */
245   tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
246   if (tmp) {
247     if (client_entry->attrs)
248       silc_attribute_payload_list_free(client_entry->attrs);
249     client_entry->attrs = silc_attribute_payload_parse(tmp, len);
250   }
251
252   client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
253
254   /* Notify application */
255   if (!cmd->callbacks_count && notify)
256     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
257                    has_channels ? &channels : NULL, mode, idle, 
258                    fingerprint, has_user_modes ? &ch_user_modes : NULL,
259                    client_entry->attrs));
260 }
261
262 /* Received reply for WHOIS command. This maybe called several times
263    for one WHOIS command as server may reply with list of results. */
264
265 SILC_CLIENT_CMD_REPLY_FUNC(whois)
266 {
267   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
268   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
269
270   COMMAND_CHECK_STATUS;
271
272   /* Save WHOIS info */
273   silc_client_command_reply_whois_save(cmd, cmd->status, TRUE);
274
275   /* Pending callbacks are not executed if this was an list entry */
276   if (cmd->status != SILC_STATUS_OK &&
277       cmd->status != SILC_STATUS_LIST_END) {
278     silc_client_command_reply_free(cmd);
279     return;
280   }
281
282  out:
283   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
284
285  err:
286   /* If we received notify for invalid ID we'll remove the ID if we
287      have it cached. */
288   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
289     SilcClientEntry client_entry;
290     SilcUInt32 tmp_len;
291     unsigned char *tmp =
292       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
293                                  2, &tmp_len);
294     if (tmp) {
295       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
296       if (client_id) {
297         client_entry = silc_client_get_client_by_id(cmd->client, conn,
298                                                     client_id);
299         if (client_entry)
300           silc_client_del_client(cmd->client, conn, client_entry);
301         silc_free(client_id);
302       }
303     }
304   }
305
306   silc_client_command_reply_free(cmd);
307 }
308
309 /* Received reply for WHOWAS command. */
310
311 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
312 {
313   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
314   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
315   SilcClientID *client_id;
316   SilcClientEntry client_entry = NULL;
317   SilcUInt32 len;
318   unsigned char *id_data;
319   char *nickname, *username;
320   char *realname = NULL;
321
322   COMMAND_CHECK_STATUS;
323
324   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
325   if (!id_data) {
326     COMMAND_REPLY_ERROR;
327     goto out;
328   }
329   
330   client_id = silc_id_payload_parse_id(id_data, len, NULL);
331   if (!client_id) {
332     COMMAND_REPLY_ERROR;
333     goto out;
334   }
335
336   /* Get the client entry, if exists */
337   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
338   silc_free(client_id);
339
340   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
341   username = silc_argument_get_arg_type(cmd->args, 4, &len);
342   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
343   if (!nickname || !username) {
344     COMMAND_REPLY_ERROR;
345     goto out;
346   }
347
348   /* Notify application. We don't save any history information to any
349      cache. Just pass the data to the application for displaying on 
350      the screen. */
351   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
352
353   /* Pending callbacks are not executed if this was an list entry */
354   if (cmd->status != SILC_STATUS_OK &&
355       cmd->status != SILC_STATUS_LIST_END) {
356     silc_client_command_reply_free(cmd);
357     return;
358   }
359
360  out:
361   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
362  err:
363   silc_client_command_reply_free(cmd);
364 }
365
366 static void 
367 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
368                                         SilcStatus status,
369                                         bool notify)
370 {
371   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
372   SilcClient client = cmd->client;
373   SilcClientID *client_id = NULL;
374   SilcServerID *server_id = NULL;
375   SilcChannelID *channel_id = NULL;
376   SilcClientEntry client_entry;
377   SilcServerEntry server_entry;
378   SilcChannelEntry channel_entry;
379   SilcUInt32 len;
380   unsigned char *id_data;
381   char *name = NULL, *info = NULL;
382   SilcIDPayload idp = NULL;
383   SilcIdType id_type;
384   
385   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
386   if (!id_data) {
387     if (notify)
388       COMMAND_REPLY_ERROR;
389     return;
390   }
391   idp = silc_id_payload_parse(id_data, len);
392   if (!idp) {
393     if (notify)
394       COMMAND_REPLY_ERROR;
395     return;
396   }
397
398   name = silc_argument_get_arg_type(cmd->args, 3, &len);
399   info = silc_argument_get_arg_type(cmd->args, 4, &len);
400
401   id_type = silc_id_payload_get_type(idp);
402
403   switch (id_type) {
404   case SILC_ID_CLIENT:
405     client_id = silc_id_payload_get_id(idp);
406
407     SILC_LOG_DEBUG(("Received client information"));
408
409     /* Check if we have this client cached already. */
410     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
411     if (!client_entry) {
412       SILC_LOG_DEBUG(("Adding new client entry"));
413       client_entry = 
414         silc_client_add_client(cmd->client, conn, name, info, NULL,
415                                silc_id_dup(client_id, id_type), 0);
416     } else {
417       silc_client_update_client(cmd->client, conn, client_entry, 
418                                 name, info, NULL, 0);
419     }
420
421     client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
422
423     /* Notify application */
424     if (notify)
425       COMMAND_REPLY((ARGS, client_entry, name, info));
426     break;
427
428   case SILC_ID_SERVER:
429     server_id = silc_id_payload_get_id(idp);
430
431     SILC_LOG_DEBUG(("Received server information"));
432
433     /* Check if we have this server cached already. */
434     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
435     if (!server_entry) {
436       SILC_LOG_DEBUG(("Adding new server entry"));
437       server_entry = silc_client_add_server(cmd->client, conn, name, info,
438                                             silc_id_dup(server_id, id_type));
439       if (!server_entry) {
440         if (notify)
441           COMMAND_REPLY_ERROR;
442         return;
443       }
444     } else {
445       silc_client_update_server(client, conn, server_entry, name, info);
446     }
447
448     server_entry->resolve_cmd_ident = 0;
449
450     /* Notify application */
451     if (notify)
452       COMMAND_REPLY((ARGS, server_entry, name, info));
453     break;
454
455   case SILC_ID_CHANNEL:
456     channel_id = silc_id_payload_get_id(idp);
457
458     SILC_LOG_DEBUG(("Received channel information"));
459
460     /* Check if we have this channel cached already. */
461     channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
462     if (!channel_entry) {
463       if (!name)
464         break;
465
466       /* Add new channel entry */
467       channel_entry = silc_client_add_channel(client, conn, name, 0,
468                                               channel_id);
469       channel_id = NULL;
470     }
471
472     /* Notify application */
473     if (notify)
474       COMMAND_REPLY((ARGS, channel_entry, name, info));
475     break;
476   }
477
478   silc_id_payload_free(idp);
479   silc_free(client_id);
480   silc_free(server_id);
481   silc_free(channel_id);
482 }
483
484 /* Received reply for IDENTIFY command. This maybe called several times
485    for one IDENTIFY command as server may reply with list of results. 
486    This is totally silent and does not print anything on screen. */
487
488 SILC_CLIENT_CMD_REPLY_FUNC(identify)
489 {
490   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
491   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
492
493   COMMAND_CHECK_STATUS;
494
495   /* Save IDENTIFY info */
496   silc_client_command_reply_identify_save(cmd, cmd->status, TRUE);
497
498   /* Pending callbacks are not executed if this was an list entry */
499   if (cmd->status != SILC_STATUS_OK &&
500       cmd->status != SILC_STATUS_LIST_END) {
501     silc_client_command_reply_free(cmd);
502     return;
503   }
504
505  out:
506   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
507
508  err:
509   /* If we received notify for invalid ID we'll remove the ID if we
510      have it cached. */
511   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
512     SilcClientEntry client_entry;
513     SilcUInt32 tmp_len;
514     unsigned char *tmp =
515       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
516                                  2, &tmp_len);
517     if (tmp) {
518       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
519       if (client_id) {
520         client_entry = silc_client_get_client_by_id(cmd->client, conn,
521                                                     client_id);
522         if (client_entry)
523           silc_client_del_client(cmd->client, conn, client_entry);
524         silc_free(client_id);
525       }
526     }
527   }
528
529   silc_client_command_reply_free(cmd);
530 }
531
532 /* Received reply for command NICK. If everything went without errors
533    we just received our new Client ID. */
534
535 SILC_CLIENT_CMD_REPLY_FUNC(nick)
536 {
537   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
538   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
539   SilcIDPayload idp;
540   unsigned char *tmp;
541   SilcUInt32 argc, len;
542
543   SILC_LOG_DEBUG(("Start"));
544
545   if (cmd->error != SILC_STATUS_OK) {
546     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
547         "Cannot set nickname: %s", 
548         silc_get_status_message(cmd->error));
549     COMMAND_REPLY_ERROR;
550     goto out;
551   }
552
553   argc = silc_argument_get_arg_num(cmd->args);
554   if (argc < 2 || argc > 3) {
555     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
556         "Cannot set nickname: bad reply to command");
557     COMMAND_REPLY_ERROR;
558     goto out;
559   }
560
561   /* Take received Client ID */
562   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
563   idp = silc_id_payload_parse(tmp, len);
564   if (!idp) {
565     COMMAND_REPLY_ERROR;
566     goto out;
567   }
568   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
569
570   /* Take the new nickname too */
571   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
572   if (tmp) {
573     silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
574     if (conn->nickname)
575       silc_free(conn->nickname);
576     conn->nickname = strdup(tmp);
577     conn->local_entry->nickname = conn->nickname;
578     silc_client_nickname_format(cmd->client, conn, conn->local_entry);
579     silc_idcache_add(conn->client_cache, strdup(tmp),
580                      conn->local_entry->id, conn->local_entry, 0, NULL);
581   }
582     
583   /* Notify application */
584   COMMAND_REPLY((ARGS, conn->local_entry, conn->local_entry->nickname));
585
586  out:
587   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
588   silc_client_command_reply_free(cmd);
589 }
590
591 /* Received reply to the LIST command. */
592
593 SILC_CLIENT_CMD_REPLY_FUNC(list)
594 {
595   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
596   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
597   unsigned char *tmp, *name, *topic;
598   SilcUInt32 usercount = 0, len;
599   SilcChannelID *channel_id = NULL;
600   SilcChannelEntry channel_entry;
601
602   COMMAND_CHECK_STATUS;
603
604   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
605   if (!tmp) {
606     COMMAND_REPLY_ERROR;
607     goto out;
608   }
609
610   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
611   if (!channel_id) {
612     COMMAND_REPLY_ERROR;
613     goto out;
614   }
615
616   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
617   if (!name) {
618     COMMAND_REPLY_ERROR;
619     goto out;
620   }
621
622   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
623   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
624   if (tmp)
625     SILC_GET32_MSB(usercount, tmp);
626
627   /* Check whether the channel exists, and add it to cache if it doesn't. */
628   channel_entry = silc_client_get_channel_by_id(cmd->client, conn, 
629                                                 channel_id);
630   if (!channel_entry) {
631     /* Add new channel entry */
632     channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
633                                             channel_id);
634     if (!channel_entry) {
635       COMMAND_REPLY_ERROR;
636       goto out;
637     }
638     channel_id = NULL;
639   }
640
641   /* Notify application */
642   COMMAND_REPLY((ARGS, channel_entry, name, topic, usercount));
643
644   /* Pending callbacks are not executed if this was an list entry */
645   if (cmd->status != SILC_STATUS_OK &&
646       cmd->status != SILC_STATUS_LIST_END) {
647     silc_client_command_reply_free(cmd);
648     return;
649   }
650
651  out:
652   silc_free(channel_id);
653   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
654  err:
655   silc_client_command_reply_free(cmd);
656 }
657
658 /* Received reply to topic command. */
659
660 SILC_CLIENT_CMD_REPLY_FUNC(topic)
661 {
662   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
663   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
664   SilcChannelEntry channel;
665   SilcChannelID *channel_id = NULL;
666   unsigned char *tmp;
667   char *topic;
668   SilcUInt32 argc, len;
669
670   if (cmd->error != SILC_STATUS_OK) {
671     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
672         "%s", silc_get_status_message(cmd->error));
673     COMMAND_REPLY_ERROR;
674     goto out;
675   }
676
677   argc = silc_argument_get_arg_num(cmd->args);
678   if (argc < 1 || argc > 3) {
679     COMMAND_REPLY_ERROR;
680     goto out;
681   }
682
683   /* Take Channel ID */
684   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
685   if (!tmp)
686     goto out;
687
688   /* Take topic */
689   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
690   if (!topic)
691     goto out;
692
693   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
694   if (!channel_id)
695     goto out;
696
697   /* Get the channel entry */
698   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
699   if (!channel) {
700     silc_free(channel_id);
701     COMMAND_REPLY_ERROR;
702     goto out;
703   }
704   
705   /* Notify application */
706   COMMAND_REPLY((ARGS, channel, topic));
707
708  out:
709   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
710   silc_client_command_reply_free(cmd);
711 }
712
713 /* Received reply to invite command. */
714
715 SILC_CLIENT_CMD_REPLY_FUNC(invite)
716 {
717   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
718   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
719   SilcChannelEntry channel;
720   SilcChannelID *channel_id;
721   unsigned char *tmp;
722   SilcUInt32 len;
723
724   if (cmd->error != SILC_STATUS_OK) {
725     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
726         "%s", silc_get_status_message(cmd->error));
727     COMMAND_REPLY_ERROR;
728     goto out;
729   }
730
731   /* Take Channel ID */
732   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
733   if (!tmp)
734     goto out;
735
736   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
737   if (!channel_id)
738     goto out;
739
740   /* Get the channel entry */
741   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
742   if (!channel) {
743     silc_free(channel_id);
744     COMMAND_REPLY_ERROR;
745     goto out;
746   }
747
748   /* Get the invite list */
749   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
750
751   /* Notify application */
752   COMMAND_REPLY((ARGS, channel, tmp));
753
754  out:
755   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
756   silc_client_command_reply_free(cmd);
757 }
758
759 /* Received reply to the KILL command. */
760  
761 SILC_CLIENT_CMD_REPLY_FUNC(kill)
762 {
763   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
764   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
765
766   if (cmd->error != SILC_STATUS_OK) {
767     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
768         "%s", silc_get_status_message(cmd->error));
769     COMMAND_REPLY_ERROR;
770     goto out;
771   }
772
773   /* Notify application */
774   COMMAND_REPLY((ARGS));
775
776  out:
777   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
778   silc_client_command_reply_free(cmd);
779 }
780
781 /* Received reply to INFO command. We receive the server ID and some
782    information about the server user requested. */
783
784 SILC_CLIENT_CMD_REPLY_FUNC(info)
785 {
786   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
787   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
788   unsigned char *tmp;
789   SilcServerEntry server;
790   SilcServerID *server_id = NULL;
791   char *server_name, *server_info;
792   SilcUInt32 len;
793
794   SILC_LOG_DEBUG(("Start"));
795
796   if (cmd->error != SILC_STATUS_OK) {
797     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
798         silc_get_status_message(cmd->error));
799     COMMAND_REPLY_ERROR;
800     goto out;
801   }
802
803   /* Get server ID */
804   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
805   if (!tmp)
806     goto out;
807
808   server_id = silc_id_payload_parse_id(tmp, len, NULL);
809   if (!server_id)
810     goto out;
811
812   /* Get server name */
813   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
814   if (!server_name)
815     goto out;
816
817   /* Get server info */
818   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
819   if (!server_info)
820     goto out;
821
822   /* See whether we have this server cached. If not create it. */
823   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
824   if (!server) {
825     SILC_LOG_DEBUG(("New server entry"));
826     server = silc_client_add_server(cmd->client, conn, server_name,
827                                     server_info,
828                                     silc_id_dup(server_id, SILC_ID_SERVER));
829     if (!server)
830       goto out;
831   }
832
833   /* Notify application */
834   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
835
836  out:
837   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
838   silc_free(server_id);
839   silc_client_command_reply_free(cmd);
840 }
841
842 /* Received reply to STATS command. */
843
844 SILC_CLIENT_CMD_REPLY_FUNC(stats)
845 {
846   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
847   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
848   unsigned char *tmp, *buf = NULL;
849   SilcUInt32 len, buf_len = 0;
850
851   if (cmd->error != SILC_STATUS_OK) {
852     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
853         "%s", silc_get_status_message(cmd->error));
854     COMMAND_REPLY_ERROR;
855     goto out;
856   }
857
858   /* Get server ID */
859   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
860   if (!tmp)
861     goto out;
862
863   /* Get statistics structure */
864   buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
865
866   /* Notify application */
867   COMMAND_REPLY((ARGS, buf, buf_len));
868
869  out:
870   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
871   silc_client_command_reply_free(cmd);
872 }
873
874 /* Received reply to PING command. The reply time is shown to user. */
875
876 SILC_CLIENT_CMD_REPLY_FUNC(ping)
877 {
878   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
879   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
880   void *id;
881   int i;
882   time_t diff, curtime;
883
884   if (cmd->error != SILC_STATUS_OK) {
885     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
886         "%s", silc_get_status_message(cmd->error));
887     COMMAND_REPLY_ERROR;
888     goto out;
889   }
890
891   curtime = time(NULL);
892   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
893                       cmd->packet->src_id_type);
894   if (!id || !conn->ping) {
895     COMMAND_REPLY_ERROR;
896     goto out;
897   }
898
899   for (i = 0; i < conn->ping_count; i++) {
900     if (!conn->ping[i].dest_id)
901       continue;
902     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
903       diff = curtime - conn->ping[i].start_time;
904       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
905           "Ping reply from %s: %d second%s", 
906           conn->ping[i].dest_name, diff, 
907           diff == 1 ? "" : "s");
908       
909       conn->ping[i].start_time = 0;
910       silc_free(conn->ping[i].dest_id);
911       conn->ping[i].dest_id = NULL;
912       silc_free(conn->ping[i].dest_name);
913       conn->ping[i].dest_name = NULL;
914       break;
915     }
916   }
917
918   silc_free(id);
919
920   /* Notify application */
921   COMMAND_REPLY((ARGS));
922
923  out:
924   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
925   silc_client_command_reply_free(cmd);
926 }
927
928 /* Received reply for JOIN command. */
929
930 SILC_CLIENT_CMD_REPLY_FUNC(join)
931 {
932   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
933   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
934   SilcChannelEntry channel;
935   SilcChannelUser chu;
936   SilcChannelID *channel_id;
937   SilcUInt32 argc, mode = 0, len, list_count;
938   char *topic, *tmp, *channel_name = NULL, *hmac;
939   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
940   int i;
941
942   SILC_LOG_DEBUG(("Start"));
943
944   if (cmd->error != SILC_STATUS_OK) {
945     if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
946       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
947           "%s", silc_get_status_message(cmd->error));
948     COMMAND_REPLY_ERROR;
949     goto out;
950   }
951
952   argc = silc_argument_get_arg_num(cmd->args);
953   if (argc < 7) {
954     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
955         "Cannot join channel: Bad reply packet");
956     COMMAND_REPLY_ERROR;
957     goto out;
958   }
959
960   /* Get channel name */
961   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
962   if (!tmp) {
963     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
964         "Cannot join channel: Bad reply packet");
965     COMMAND_REPLY_ERROR;
966     goto out;
967   }
968   channel_name = tmp;
969
970   /* Get Channel ID */
971   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
972   if (!tmp) {
973     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
974         "Cannot join channel: Bad reply packet");
975     COMMAND_REPLY_ERROR;
976     goto out;
977   }
978   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
979   if (!channel_id) {
980     COMMAND_REPLY_ERROR;
981     goto out;
982   }
983
984   /* Get channel mode */
985   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
986   if (tmp)
987     SILC_GET32_MSB(mode, tmp);
988
989   /* Get channel key */
990   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
991   if (tmp) {
992     keyp = silc_buffer_alloc(len);
993     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
994     silc_buffer_put(keyp, tmp, len);
995   }
996
997   /* Get topic */
998   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
999
1000   /* Check whether we have this channel entry already. */
1001   channel = silc_client_get_channel(cmd->client, conn, channel_name);
1002   if (channel) {
1003     if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1004       silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1005   } else {
1006     /* Create new channel entry */
1007     channel = silc_client_add_channel(cmd->client, conn, channel_name, 
1008                                       mode, channel_id);
1009   }
1010
1011   conn->current_channel = channel;
1012
1013   /* Get hmac */
1014   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1015   if (hmac) {
1016     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1017       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
1018           "Cannot join channel: Unsupported HMAC `%s'", hmac);
1019       COMMAND_REPLY_ERROR;
1020       goto out;
1021     }
1022   }
1023
1024   /* Get the list count */
1025   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1026   if (!tmp)
1027     goto out;
1028   SILC_GET32_MSB(list_count, tmp);
1029
1030   /* Get Client ID list */
1031   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1032   if (!tmp)
1033     goto out;
1034
1035   client_id_list = silc_buffer_alloc(len);
1036   silc_buffer_pull_tail(client_id_list, len);
1037   silc_buffer_put(client_id_list, tmp, len);
1038
1039   /* Get client mode list */
1040   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1041   if (!tmp)
1042     goto out;
1043
1044   client_mode_list = silc_buffer_alloc(len);
1045   silc_buffer_pull_tail(client_mode_list, len);
1046   silc_buffer_put(client_mode_list, tmp, len);
1047
1048   /* Add clients we received in the reply to the channel */
1049   for (i = 0; i < list_count; i++) {
1050     SilcUInt16 idp_len;
1051     SilcUInt32 mode;
1052     SilcClientID *client_id;
1053     SilcClientEntry client_entry;
1054
1055     /* Client ID */
1056     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1057     idp_len += 4;
1058     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1059     if (!client_id)
1060       continue;
1061
1062     /* Mode */
1063     SILC_GET32_MSB(mode, client_mode_list->data);
1064
1065     /* Check if we have this client cached already. */
1066     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1067     if (!client_entry) {
1068       /* No, we don't have it, add entry for it. */
1069       client_entry = 
1070         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1071                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1072     }
1073
1074     /* Join client to the channel */
1075     if (!silc_client_on_channel(channel, client_entry)) {
1076       chu = silc_calloc(1, sizeof(*chu));
1077       chu->client = client_entry;
1078       chu->channel = channel;
1079       chu->mode = mode;
1080       silc_hash_table_add(channel->user_list, client_entry, chu);
1081       silc_hash_table_add(client_entry->channels, channel, chu);
1082     }
1083
1084     silc_free(client_id);
1085     silc_buffer_pull(client_id_list, idp_len);
1086     silc_buffer_pull(client_mode_list, 4);
1087   }
1088   silc_buffer_push(client_id_list, client_id_list->data - 
1089                    client_id_list->head);
1090   silc_buffer_push(client_mode_list, client_mode_list->data - 
1091                    client_mode_list->head);
1092
1093   /* Save channel key */
1094   if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1095     silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1096
1097   /* Notify application */
1098   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1099                  keyp ? keyp->head : NULL, NULL,
1100                  NULL, topic, hmac, list_count, client_id_list, 
1101                  client_mode_list));
1102
1103  out:
1104   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1105   silc_client_command_reply_free(cmd);
1106
1107   if (keyp)
1108     silc_buffer_free(keyp);
1109   if (client_id_list)
1110     silc_buffer_free(client_id_list);
1111   if (client_mode_list)
1112     silc_buffer_free(client_mode_list);
1113 }
1114
1115 /* Received reply for MOTD command */
1116
1117 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1118 {
1119   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1120   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1121   SilcUInt32 argc, i;
1122   char *motd = NULL, *cp, line[256];
1123
1124   if (cmd->error != SILC_STATUS_OK) {
1125     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1126         "%s", silc_get_status_message(cmd->error));
1127     COMMAND_REPLY_ERROR;
1128     return;
1129   }
1130
1131   argc = silc_argument_get_arg_num(cmd->args);
1132   if (argc > 3) {
1133     COMMAND_REPLY_ERROR;
1134     goto out;
1135   }
1136
1137   if (argc == 3) {
1138     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1139     if (!motd) {
1140       COMMAND_REPLY_ERROR;
1141       goto out;
1142     }
1143
1144     i = 0;
1145     cp = motd;
1146     while(cp[i] != 0) {
1147       if (cp[i++] == '\n') {
1148         memset(line, 0, sizeof(line));
1149         silc_strncat(line, sizeof(line), cp, i - 1);
1150         cp += i;
1151         
1152         if (i == 2)
1153           line[0] = ' ';
1154         
1155         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1156         
1157         if (!strlen(cp))
1158           break;
1159         i = 0;
1160       }
1161     }
1162   }
1163
1164   /* Notify application */
1165   COMMAND_REPLY((ARGS, motd));
1166
1167  out:
1168   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1169   silc_client_command_reply_free(cmd);
1170 }
1171
1172 /* Received reply tot he UMODE command. Save the current user mode */
1173
1174 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1175 {
1176   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1177   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1178   unsigned char *tmp;
1179   SilcUInt32 mode;
1180
1181   if (cmd->error != SILC_STATUS_OK) {
1182     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1183         "%s", silc_get_status_message(cmd->error));
1184     COMMAND_REPLY_ERROR;
1185     goto out;
1186   }
1187
1188   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1189   if (!tmp) {
1190     COMMAND_REPLY_ERROR;
1191     goto out;
1192   }
1193
1194   SILC_GET32_MSB(mode, tmp);
1195   conn->local_entry->mode = mode;
1196
1197   /* Notify application */
1198   COMMAND_REPLY((ARGS, mode));
1199
1200  out:
1201   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1202   silc_client_command_reply_free(cmd);
1203 }
1204
1205 /* Received reply for CMODE command. */
1206
1207 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1208 {
1209   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1210   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1211   unsigned char *tmp;
1212   SilcUInt32 mode;
1213   SilcChannelID *channel_id;
1214   SilcChannelEntry channel;
1215   SilcUInt32 len;
1216
1217   if (cmd->error != SILC_STATUS_OK) {
1218     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1219         "%s", silc_get_status_message(cmd->error));
1220     COMMAND_REPLY_ERROR;
1221     goto out;
1222   }
1223
1224   /* Take Channel ID */
1225   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1226   if (!tmp)
1227     goto out;
1228   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1229   if (!channel_id)
1230     goto out;
1231
1232   /* Get the channel entry */
1233   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1234   if (!channel) {
1235     silc_free(channel_id);
1236     COMMAND_REPLY_ERROR;
1237     goto out;
1238   }
1239   
1240   /* Get channel mode */
1241   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1242   if (!tmp) {
1243     silc_free(channel_id);
1244     COMMAND_REPLY_ERROR;
1245     goto out;
1246   }
1247
1248   /* Save the mode */
1249   SILC_GET32_MSB(mode, tmp);
1250   channel->mode = mode;
1251
1252   /* Notify application */
1253   COMMAND_REPLY((ARGS, channel, mode));
1254
1255   silc_free(channel_id);
1256
1257  out:
1258   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1259   silc_client_command_reply_free(cmd);
1260 }
1261
1262 /* Received reply for CUMODE command */
1263
1264 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1265 {
1266   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1267   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1268   SilcClientID *client_id;
1269   SilcChannelID *channel_id;
1270   SilcClientEntry client_entry;
1271   SilcChannelEntry channel;
1272   SilcChannelUser chu;
1273   unsigned char *modev, *tmp, *id;
1274   SilcUInt32 len, mode;
1275   
1276   if (cmd->error != SILC_STATUS_OK) {
1277     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1278         "%s", silc_get_status_message(cmd->error));
1279     COMMAND_REPLY_ERROR;
1280     goto out;
1281   }
1282   
1283   /* Get channel mode */
1284   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1285   if (!modev) {
1286     COMMAND_REPLY_ERROR;
1287     goto out;
1288   }
1289
1290   /* Take Channel ID */
1291   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1292   if (!tmp)
1293     goto out;
1294   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1295   if (!channel_id)
1296     goto out;
1297
1298   /* Get the channel entry */
1299   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1300   if (!channel) {
1301     silc_free(channel_id);
1302     COMMAND_REPLY_ERROR;
1303     goto out;
1304   }
1305   
1306   /* Get Client ID */
1307   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1308   if (!id) {
1309     silc_free(channel_id);
1310     COMMAND_REPLY_ERROR;
1311     goto out;
1312   }
1313   client_id = silc_id_payload_parse_id(id, len, NULL);
1314   if (!client_id) {
1315     silc_free(channel_id);
1316     COMMAND_REPLY_ERROR;
1317     goto out;
1318   }
1319   
1320   /* Get client entry */
1321   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1322   if (!client_entry) {
1323     silc_free(channel_id);
1324     silc_free(client_id);
1325     COMMAND_REPLY_ERROR;
1326     goto out;
1327   }
1328
1329   /* Save the mode */
1330   SILC_GET32_MSB(mode, modev);
1331   chu = silc_client_on_channel(channel, client_entry);
1332   if (chu)
1333     chu->mode = mode;
1334
1335   /* Notify application */
1336   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1337   silc_free(client_id);
1338   silc_free(channel_id);
1339   
1340  out:
1341   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1342   silc_client_command_reply_free(cmd);
1343 }
1344
1345 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1346 {
1347   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1348   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1349
1350   if (cmd->error != SILC_STATUS_OK) {
1351     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1352         "%s", silc_get_status_message(cmd->error));
1353     COMMAND_REPLY_ERROR;
1354     goto out;
1355   }
1356
1357   /* Notify application */
1358   COMMAND_REPLY((ARGS));
1359
1360  out:
1361   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1362   silc_client_command_reply_free(cmd);
1363 }
1364
1365 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1366 {
1367   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1368   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1369
1370   if (cmd->error != SILC_STATUS_OK) {
1371     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1372         "%s", silc_get_status_message(cmd->error));
1373     COMMAND_REPLY_ERROR;
1374     goto out;
1375   }
1376
1377   /* Notify application */
1378   COMMAND_REPLY((ARGS));
1379
1380  out:
1381   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1382   silc_client_command_reply_free(cmd);
1383 }
1384
1385 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1386 {
1387   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1388   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1389
1390   if (cmd->error != SILC_STATUS_OK) {
1391     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1392         "%s", silc_get_status_message(cmd->error));
1393     COMMAND_REPLY_ERROR;
1394     goto out;
1395   }
1396
1397   /* Notify application */
1398   COMMAND_REPLY((ARGS));
1399
1400  out:
1401   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1402   silc_client_command_reply_free(cmd);
1403 }
1404
1405 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1406 {
1407   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1408   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1409   SilcBuffer detach;
1410
1411   if (cmd->error != SILC_STATUS_OK) {
1412     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1413         "%s", silc_get_status_message(cmd->error));
1414     COMMAND_REPLY_ERROR;
1415     goto out;
1416   }
1417
1418   /* Notify application */
1419   COMMAND_REPLY((ARGS));
1420
1421   /* Generate the detachment data and deliver it to the client in the
1422      detach client operation */
1423   detach = silc_client_get_detach_data(cmd->client, conn);
1424   if (detach) {
1425     cmd->client->internal->ops->detach(cmd->client, conn, 
1426                                        detach->data, detach->len);
1427     silc_buffer_free(detach);
1428   }
1429
1430  out:
1431   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1432   silc_client_command_reply_free(cmd);
1433 }
1434
1435 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1436 {
1437   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1438   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1439
1440   if (cmd->error != SILC_STATUS_OK) {
1441     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1442         "%s", silc_get_status_message(cmd->error));
1443     COMMAND_REPLY_ERROR;
1444     goto out;
1445   }
1446
1447   /* Notify application */
1448   COMMAND_REPLY((ARGS));
1449
1450  out:
1451   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1452   silc_client_command_reply_free(cmd);
1453 }
1454
1455 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1456 {
1457   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1458   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1459   SilcChannelEntry channel;
1460   SilcChannelID *channel_id;
1461   unsigned char *tmp;
1462   SilcUInt32 len;
1463
1464   if (cmd->error != SILC_STATUS_OK) {
1465     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1466         "%s", silc_get_status_message(cmd->error));
1467     COMMAND_REPLY_ERROR;
1468     goto out;
1469   }
1470
1471   /* Take Channel ID */
1472   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1473   if (!tmp)
1474     goto out;
1475
1476   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1477   if (!channel_id)
1478     goto out;
1479
1480   /* Get the channel entry */
1481   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1482   if (!channel) {
1483     silc_free(channel_id);
1484     COMMAND_REPLY_ERROR;
1485     goto out;
1486   }
1487   
1488   /* Get the ban list */
1489   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1490
1491   /* Notify application */
1492   COMMAND_REPLY((ARGS, channel, tmp));
1493
1494  out:
1495   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1496   silc_client_command_reply_free(cmd);
1497 }
1498
1499 /* Reply to LEAVE command. */
1500
1501 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1502 {
1503   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1504   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1505   SilcChannelID *channel_id;
1506   SilcChannelEntry channel = NULL;
1507   unsigned char *tmp;
1508   SilcUInt32 len;
1509
1510   if (cmd->error != SILC_STATUS_OK) {
1511     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1512         "%s", silc_get_status_message(cmd->error));
1513     COMMAND_REPLY_ERROR;
1514     goto out;
1515   }
1516
1517   /* From protocol version 1.1 we get the channel ID of the left channel */
1518   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1519   if (tmp) {
1520     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1521     if (!channel_id)
1522       goto out;
1523
1524     /* Get the channel entry */
1525     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1526     if (!channel) {
1527       silc_free(channel_id);
1528       COMMAND_REPLY_ERROR;
1529       goto out;
1530     }
1531
1532     silc_free(channel_id);
1533   }
1534
1535   /* Notify application */
1536   COMMAND_REPLY((ARGS, channel));
1537
1538  out:
1539   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1540   silc_client_command_reply_free(cmd);
1541 }
1542
1543 /* Channel resolving callback for USERS command reply. */
1544
1545 static void silc_client_command_reply_users_cb(SilcClient client,
1546                                                SilcClientConnection conn,
1547                                                SilcChannelEntry *channels,
1548                                                SilcUInt32 channels_count,
1549                                                void *context)
1550 {
1551   if (!channels_count) {
1552     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1553     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1554
1555     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1556     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1557         "%s", silc_get_status_message(cmd->error));
1558     COMMAND_REPLY_ERROR;
1559     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1560     silc_client_command_reply_free(cmd);
1561     return;
1562   }
1563
1564   silc_client_command_reply_users(context, NULL);
1565 }
1566
1567 static int
1568 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1569                                      SilcStatus status,
1570                                      bool notify,
1571                                      SilcGetChannelCallback get_channel,
1572                                      SilcCommandCb get_clients)
1573 {
1574   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1575   SilcChannelEntry channel;
1576   SilcClientEntry client_entry;
1577   SilcChannelUser chu;
1578   SilcChannelID *channel_id = NULL;
1579   SilcBufferStruct client_id_list, client_mode_list;
1580   unsigned char *tmp;
1581   SilcUInt32 tmp_len, list_count;
1582   int i;
1583   unsigned char **res_argv = NULL;
1584   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1585   bool wait_res = FALSE;
1586
1587   SILC_LOG_DEBUG(("Start"));
1588
1589   /* Get channel ID */
1590   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1591   if (!tmp) {
1592     COMMAND_REPLY_ERROR;
1593     goto out;
1594   }
1595   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1596   if (!channel_id) {
1597     COMMAND_REPLY_ERROR;
1598     goto out;
1599   }
1600   
1601   /* Get the list count */
1602   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1603   if (!tmp) {
1604     COMMAND_REPLY_ERROR;
1605     goto out;
1606   }
1607   SILC_GET32_MSB(list_count, tmp);
1608
1609   /* Get Client ID list */
1610   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1611   if (!tmp) {
1612     COMMAND_REPLY_ERROR;
1613     goto out;
1614   }
1615   silc_buffer_set(&client_id_list, tmp, tmp_len);
1616
1617   /* Get client mode list */
1618   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1619   if (!tmp) {
1620     COMMAND_REPLY_ERROR;
1621     goto out;
1622   }
1623   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1624
1625   /* Get channel entry */
1626   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1627   if (!channel) {
1628     /* Resolve the channel from server */
1629     silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1630                                           get_channel, cmd);
1631     silc_free(channel_id);
1632     return 1;
1633   }
1634
1635   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1636
1637   /* Cache the received Client ID's and modes. */
1638   for (i = 0; i < list_count; i++) {
1639     SilcUInt16 idp_len;
1640     SilcUInt32 mode;
1641     SilcClientID *client_id;
1642
1643     /* Client ID */
1644     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1645     idp_len += 4;
1646     client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1647     if (!client_id)
1648       continue;
1649
1650     /* Mode */
1651     SILC_GET32_MSB(mode, client_mode_list.data);
1652
1653     /* Check if we have this client cached already. */
1654     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1655     if (!client_entry || !client_entry->username || !client_entry->realname) {
1656       /* No we don't have it (or it is incomplete in information), query
1657          it from the server. Assemble argument table that will be sent
1658          for the WHOIS command later. */
1659       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1660                               (res_argc + 1));
1661       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1662                                    (res_argc + 1));
1663       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1664                                     (res_argc + 1));
1665       res_argv[res_argc] = client_id_list.data;
1666       res_argv_lens[res_argc] = idp_len;
1667       res_argv_types[res_argc] = res_argc + 4;
1668       res_argc++;
1669     } else {
1670       if (!silc_client_on_channel(channel, client_entry)) {
1671         chu = silc_calloc(1, sizeof(*chu));
1672         chu->client = client_entry;
1673         chu->mode = mode;
1674         chu->channel = channel;
1675         silc_hash_table_add(channel->user_list, client_entry, chu);
1676         silc_hash_table_add(client_entry->channels, channel, chu);
1677       }
1678     }
1679
1680     silc_free(client_id);
1681     silc_buffer_pull(&client_id_list, idp_len);
1682     silc_buffer_pull(&client_mode_list, 4);
1683   }
1684
1685   /* Query the client information from server if the list included clients
1686      that we don't know about. */
1687   if (res_argc) {
1688     SilcBuffer res_cmd;
1689
1690     /* Send the WHOIS command to server */
1691     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1692                                  silc_client_command_reply_whois_i, 0,
1693                                  ++conn->cmd_ident);
1694     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1695                                           res_argc, res_argv, res_argv_lens,
1696                                           res_argv_types, conn->cmd_ident);
1697     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1698                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1699                             TRUE);
1700
1701     /* Register pending command callback. After we've received the WHOIS
1702        command reply we will reprocess this command reply by re-calling this
1703        USERS command reply callback. */
1704     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1705                                 get_clients, cmd);
1706
1707     silc_buffer_free(res_cmd);
1708     silc_free(channel_id);
1709     silc_free(res_argv);
1710     silc_free(res_argv_lens);
1711     silc_free(res_argv_types);
1712     return 1;
1713   }
1714
1715   if (wait_res)
1716     return 1;
1717
1718   silc_buffer_push(&client_id_list, (client_id_list.data - 
1719                                      client_id_list.head));
1720   silc_buffer_push(&client_mode_list, (client_mode_list.data - 
1721                                        client_mode_list.head));
1722
1723   /* Notify application */
1724   if (notify)
1725     COMMAND_REPLY((ARGS, channel, list_count, &client_id_list, 
1726                    &client_mode_list));
1727
1728  out:
1729   silc_free(channel_id);
1730   return 0;
1731 }
1732
1733 /* Reply to USERS command. Received list of client ID's and theirs modes
1734    on the channel we requested. */
1735
1736 SILC_CLIENT_CMD_REPLY_FUNC(users)
1737 {
1738   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1739   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1740
1741   SILC_LOG_DEBUG(("Start"));
1742
1743   if (cmd->error != SILC_STATUS_OK) {
1744     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1745         "%s", silc_get_status_message(cmd->error));
1746     COMMAND_REPLY_ERROR;
1747     goto out;
1748   }
1749
1750   if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
1751                                            silc_client_command_reply_users_cb,
1752                                            silc_client_command_reply_users))
1753     return;
1754
1755  out:
1756   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1757   silc_client_command_reply_free(cmd);
1758 }
1759
1760 /* Received command reply to GETKEY command. WE've received the remote
1761    client's public key. */
1762
1763 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1764 {
1765   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1766   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1767   SilcIDPayload idp = NULL;
1768   SilcClientID *client_id = NULL;
1769   SilcClientEntry client_entry;
1770   SilcServerID *server_id = NULL;
1771   SilcServerEntry server_entry;
1772   SilcSKEPKType type;
1773   unsigned char *tmp, *pk;
1774   SilcUInt32 len;
1775   SilcUInt16 pk_len;
1776   SilcIdType id_type;
1777   SilcPublicKey public_key = NULL;
1778
1779   SILC_LOG_DEBUG(("Start"));
1780
1781   if (cmd->error != SILC_STATUS_OK) {
1782     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1783         "%s", silc_get_status_message(cmd->error));
1784     COMMAND_REPLY_ERROR;
1785     goto out;
1786   }
1787
1788   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1789   if (!tmp) {
1790     COMMAND_REPLY_ERROR;
1791     goto out;
1792   }
1793   idp = silc_id_payload_parse(tmp, len);
1794   if (!idp) {
1795     COMMAND_REPLY_ERROR;
1796     goto out;
1797   }
1798
1799   /* Get the public key payload */
1800   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1801   if (tmp) {
1802     /* Decode the public key */
1803     SILC_GET16_MSB(pk_len, tmp);
1804     SILC_GET16_MSB(type, tmp + 2);
1805     pk = tmp + 4;
1806     
1807     if (type == SILC_SKE_PK_TYPE_SILC)
1808       if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1809         public_key = NULL;
1810   } 
1811    
1812   id_type = silc_id_payload_get_type(idp);
1813   if (id_type == SILC_ID_CLIENT) {
1814     /* Received client's public key */
1815     client_id = silc_id_payload_get_id(idp);
1816     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1817     if (!client_entry) {
1818       COMMAND_REPLY_ERROR;
1819       goto out;
1820     }
1821
1822     /* Notify application */
1823     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1824   } else if (id_type == SILC_ID_SERVER) {
1825     /* Received server's public key */
1826     server_id = silc_id_payload_get_id(idp);
1827     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1828     if (!server_entry) {
1829       COMMAND_REPLY_ERROR;
1830       goto out;
1831     }
1832
1833     /* Notify application */
1834     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1835   }
1836
1837  out:
1838   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1839   if (idp)
1840     silc_id_payload_free(idp);
1841   if (public_key)
1842     silc_pkcs_public_key_free(public_key);
1843   silc_free(client_id);
1844   silc_free(server_id);
1845   silc_client_command_reply_free(cmd);
1846 }
1847
1848 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1849 {
1850   silc_client_command_reply_free(context);
1851 }
1852
1853
1854 /******************************************************************************
1855
1856                       Internal command reply functions
1857
1858 ******************************************************************************/
1859
1860 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1861 {
1862   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1863   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1864
1865   COMMAND_CHECK_STATUS_I;
1866
1867   /* Save WHOIS info */
1868   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1869
1870   /* Pending callbacks are not executed if this was an list entry */
1871   if (cmd->status != SILC_STATUS_OK &&
1872       cmd->status != SILC_STATUS_LIST_END) {
1873     silc_client_command_reply_free(cmd);
1874     return;
1875   }
1876
1877  out:
1878   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1879
1880  err:
1881   /* If we received notify for invalid ID we'll remove the ID if we
1882      have it cached. */
1883   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1884     SilcClientEntry client_entry;
1885     SilcUInt32 tmp_len;
1886     unsigned char *tmp =
1887       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1888                                  2, &tmp_len);
1889     if (tmp) {
1890       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1891       if (client_id) {
1892         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1893                                                     client_id);
1894         if (client_entry)
1895           silc_client_del_client(cmd->client, conn, client_entry);
1896         silc_free(client_id);
1897       }
1898     }
1899   }
1900
1901   /* Unregister this command reply */
1902   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1903                                  NULL, silc_client_command_reply_whois_i,
1904                                  cmd->ident);
1905
1906   silc_client_command_reply_free(cmd);
1907 }
1908
1909 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1910 {
1911   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1912   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1913
1914   COMMAND_CHECK_STATUS_I;
1915
1916   /* Save IDENTIFY info */
1917   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1918
1919   /* Pending callbacks are not executed if this was an list entry */
1920   if (cmd->status != SILC_STATUS_OK &&
1921       cmd->status != SILC_STATUS_LIST_END) {
1922     silc_client_command_reply_free(cmd);
1923     return;
1924   }
1925
1926  out:
1927   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1928
1929  err:
1930   /* If we received notify for invalid ID we'll remove the ID if we
1931      have it cached. */
1932   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1933     SilcClientEntry client_entry;
1934     SilcUInt32 tmp_len;
1935     unsigned char *tmp =
1936       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1937                                  2, &tmp_len);
1938     if (tmp) {
1939       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1940       if (client_id) {
1941         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1942                                                     client_id);
1943         if (client_entry)
1944           silc_client_del_client(cmd->client, conn, client_entry);
1945         silc_free(client_id);
1946       }
1947     }
1948   }
1949
1950   /* Unregister this command reply */
1951   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1952                                  NULL, silc_client_command_reply_identify_i,
1953                                  cmd->ident);
1954
1955   silc_client_command_reply_free(cmd);
1956 }
1957
1958 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1959 {
1960   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1961   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1962   unsigned char *tmp;
1963   SilcServerEntry server;
1964   SilcServerID *server_id = NULL;
1965   char *server_name, *server_info;
1966   SilcUInt32 len;
1967
1968   COMMAND_CHECK_STATUS_I;
1969
1970   /* Get server ID */
1971   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1972   if (!tmp)
1973     goto out;
1974
1975   server_id = silc_id_payload_parse_id(tmp, len, NULL);
1976   if (!server_id)
1977     goto out;
1978
1979   /* Get server name */
1980   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1981   if (!server_name)
1982     goto out;
1983
1984   /* Get server info */
1985   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
1986   if (!server_info)
1987     goto out;
1988
1989   /* See whether we have this server cached. If not create it. */
1990   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
1991   if (!server) {
1992     SILC_LOG_DEBUG(("New server entry"));
1993     silc_client_add_server(cmd->client, conn, server_name, server_info,
1994                            silc_id_dup(server_id, SILC_ID_SERVER));
1995   }
1996   
1997  out:
1998   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
1999   silc_free(server_id);
2000  err:
2001   silc_client_command_reply_free(cmd);
2002 }
2003
2004 static void silc_client_command_reply_users_i_cb(SilcClient client,
2005                                                  SilcClientConnection conn,
2006                                                  SilcChannelEntry *channels,
2007                                                  SilcUInt32 channels_count,
2008                                                  void *context)
2009 {
2010   if (!channels_count) {
2011     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2012     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2013
2014     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2015     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2016         "%s", silc_get_status_message(cmd->error));
2017     COMMAND_REPLY_ERROR;
2018     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2019     silc_client_command_reply_free(cmd);
2020     return;
2021   }
2022
2023   silc_client_command_reply_users_i(context, NULL);
2024 }
2025
2026 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2027 {
2028   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2029
2030   COMMAND_CHECK_STATUS_I;
2031
2032   /* Save USERS info */
2033   if (silc_client_command_reply_users_save(
2034                                     cmd, cmd->status, FALSE,
2035                                     silc_client_command_reply_users_i_cb,
2036                                     silc_client_command_reply_users_i))
2037     return;
2038
2039  out:
2040   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2041
2042  err:
2043   /* Unregister this command reply */
2044   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2045                                  NULL, silc_client_command_reply_users_i,
2046                                  cmd->ident);
2047
2048   silc_client_command_reply_free(cmd);
2049 }
2050
2051 /* Private range commands, specific to this implementation (and compatible
2052    with SILC Server >= 0.9). */
2053
2054 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2055 {
2056   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2057   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2058
2059   if (cmd->error != SILC_STATUS_OK) {
2060     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2061         "%s", silc_get_status_message(cmd->error));
2062     COMMAND_REPLY_ERROR;
2063     goto out;
2064   }
2065
2066   /* Notify application */
2067   COMMAND_REPLY((ARGS));
2068
2069  out:
2070   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2071   silc_client_command_reply_free(cmd);
2072 }
2073
2074 SILC_CLIENT_CMD_REPLY_FUNC(close)
2075 {
2076   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2077   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2078
2079   if (cmd->error != SILC_STATUS_OK) {
2080     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2081         "%s", silc_get_status_message(cmd->error));
2082     COMMAND_REPLY_ERROR;
2083     goto out;
2084   }
2085
2086   /* Notify application */
2087   COMMAND_REPLY((ARGS));
2088
2089  out:
2090   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2091   silc_client_command_reply_free(cmd);
2092 }
2093  
2094 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2095 {
2096   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2097   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2098
2099   if (cmd->error != SILC_STATUS_OK) {
2100     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2101         "%s", silc_get_status_message(cmd->error));
2102     COMMAND_REPLY_ERROR;
2103     goto out;
2104   }
2105
2106   /* Notify application */
2107   COMMAND_REPLY((ARGS));
2108
2109  out:
2110   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2111   silc_client_command_reply_free(cmd);
2112 }