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