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   /* Take the new nickname too */
519   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
520   if (tmp) {
521     silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
522     if (conn->nickname)
523       silc_free(conn->nickname);
524     conn->nickname = strdup(tmp);
525     conn->local_entry->nickname = conn->nickname;
526     silc_client_nickname_format(cmd->client, conn, conn->local_entry);
527     silc_idcache_add(conn->client_cache, strdup(tmp),
528                      conn->local_entry->id, conn->local_entry, 0, NULL);
529   }
530     
531   /* Notify application */
532   COMMAND_REPLY((ARGS, conn->local_entry, conn->local_entry->nickname));
533
534  out:
535   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
536   silc_client_command_reply_free(cmd);
537 }
538
539 /* Received reply to the LIST command. */
540
541 SILC_CLIENT_CMD_REPLY_FUNC(list)
542 {
543   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
544   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
545   unsigned char *tmp, *name, *topic;
546   SilcUInt32 usercount = 0, len;
547   SilcChannelID *channel_id = NULL;
548   SilcChannelEntry channel_entry;
549
550   COMMAND_CHECK_STATUS;
551
552   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
553   if (!tmp) {
554     COMMAND_REPLY_ERROR;
555     goto out;
556   }
557
558   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
559   if (!channel_id) {
560     COMMAND_REPLY_ERROR;
561     goto out;
562   }
563
564   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
565   if (!name) {
566     COMMAND_REPLY_ERROR;
567     goto out;
568   }
569
570   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
571   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
572   if (tmp)
573     SILC_GET32_MSB(usercount, tmp);
574
575   /* Check whether the channel exists, and add it to cache if it doesn't. */
576   channel_entry = silc_client_get_channel_by_id(cmd->client, conn, 
577                                                 channel_id);
578   if (!channel_entry) {
579     /* Add new channel entry */
580     channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
581                                             channel_id);
582     if (!channel_entry) {
583       COMMAND_REPLY_ERROR;
584       goto out;
585     }
586     channel_id = NULL;
587   }
588
589   /* Notify application */
590   COMMAND_REPLY((ARGS, channel_entry, name, topic, usercount));
591
592   /* Pending callbacks are not executed if this was an list entry */
593   if (cmd->status != SILC_STATUS_OK &&
594       cmd->status != SILC_STATUS_LIST_END) {
595     silc_client_command_reply_free(cmd);
596     return;
597   }
598
599  out:
600   silc_free(channel_id);
601   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
602   silc_client_command_reply_free(cmd);
603 }
604
605 /* Received reply to topic command. */
606
607 SILC_CLIENT_CMD_REPLY_FUNC(topic)
608 {
609   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
610   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
611   SilcChannelEntry channel;
612   SilcChannelID *channel_id = NULL;
613   unsigned char *tmp;
614   char *topic;
615   SilcUInt32 argc, len;
616
617   if (cmd->error != SILC_STATUS_OK) {
618     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
619         "%s", silc_get_status_message(cmd->error));
620     COMMAND_REPLY_ERROR;
621     goto out;
622   }
623
624   argc = silc_argument_get_arg_num(cmd->args);
625   if (argc < 1 || argc > 3) {
626     COMMAND_REPLY_ERROR;
627     goto out;
628   }
629
630   /* Take Channel ID */
631   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
632   if (!tmp)
633     goto out;
634
635   /* Take topic */
636   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
637   if (!topic)
638     goto out;
639
640   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
641   if (!channel_id)
642     goto out;
643
644   /* Get the channel entry */
645   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
646   if (!channel) {
647     silc_free(channel_id);
648     COMMAND_REPLY_ERROR;
649     goto out;
650   }
651   
652   /* Notify application */
653   COMMAND_REPLY((ARGS, channel, topic));
654
655  out:
656   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
657   silc_client_command_reply_free(cmd);
658 }
659
660 /* Received reply to invite command. */
661
662 SILC_CLIENT_CMD_REPLY_FUNC(invite)
663 {
664   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
665   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
666   SilcChannelEntry channel;
667   SilcChannelID *channel_id;
668   unsigned char *tmp;
669   SilcUInt32 len;
670
671   if (cmd->error != SILC_STATUS_OK) {
672     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
673         "%s", silc_get_status_message(cmd->error));
674     COMMAND_REPLY_ERROR;
675     goto out;
676   }
677
678   /* Take Channel ID */
679   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
680   if (!tmp)
681     goto out;
682
683   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
684   if (!channel_id)
685     goto out;
686
687   /* Get the channel entry */
688   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
689   if (!channel) {
690     silc_free(channel_id);
691     COMMAND_REPLY_ERROR;
692     goto out;
693   }
694
695   /* Get the invite list */
696   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
697
698   /* Notify application */
699   COMMAND_REPLY((ARGS, channel, tmp));
700
701  out:
702   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
703   silc_client_command_reply_free(cmd);
704 }
705
706 /* Received reply to the KILL command. */
707  
708 SILC_CLIENT_CMD_REPLY_FUNC(kill)
709 {
710   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
711   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
712
713   if (cmd->error != SILC_STATUS_OK) {
714     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
715         "%s", silc_get_status_message(cmd->error));
716     COMMAND_REPLY_ERROR;
717     goto out;
718   }
719
720   /* Notify application */
721   COMMAND_REPLY((ARGS));
722
723  out:
724   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
725   silc_client_command_reply_free(cmd);
726 }
727
728 /* Received reply to INFO command. We receive the server ID and some
729    information about the server user requested. */
730
731 SILC_CLIENT_CMD_REPLY_FUNC(info)
732 {
733   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
734   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
735   unsigned char *tmp;
736   SilcServerEntry server;
737   SilcServerID *server_id = NULL;
738   char *server_name, *server_info;
739   SilcUInt32 len;
740
741   SILC_LOG_DEBUG(("Start"));
742
743   if (cmd->error != SILC_STATUS_OK) {
744     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
745         silc_get_status_message(cmd->error));
746     COMMAND_REPLY_ERROR;
747     goto out;
748   }
749
750   /* Get server ID */
751   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
752   if (!tmp)
753     goto out;
754
755   server_id = silc_id_payload_parse_id(tmp, len, NULL);
756   if (!server_id)
757     goto out;
758
759   /* Get server name */
760   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
761   if (!server_name)
762     goto out;
763
764   /* Get server info */
765   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
766   if (!server_info)
767     goto out;
768
769   /* See whether we have this server cached. If not create it. */
770   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
771   if (!server) {
772     SILC_LOG_DEBUG(("New server entry"));
773     server = silc_client_add_server(cmd->client, conn, server_name,
774                                     server_info,
775                                     silc_id_dup(server_id, SILC_ID_SERVER));
776     if (!server)
777       goto out;
778   }
779
780   /* Notify application */
781   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
782
783  out:
784   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
785   silc_free(server_id);
786   silc_client_command_reply_free(cmd);
787 }
788
789 /* Received reply to PING command. The reply time is shown to user. */
790
791 SILC_CLIENT_CMD_REPLY_FUNC(ping)
792 {
793   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
794   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
795   void *id;
796   int i;
797   time_t diff, curtime;
798
799   if (cmd->error != SILC_STATUS_OK) {
800     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
801         "%s", silc_get_status_message(cmd->error));
802     COMMAND_REPLY_ERROR;
803     goto out;
804   }
805
806   curtime = time(NULL);
807   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
808                       cmd->packet->src_id_type);
809   if (!id || !conn->ping) {
810     COMMAND_REPLY_ERROR;
811     goto out;
812   }
813
814   for (i = 0; i < conn->ping_count; i++) {
815     if (!conn->ping[i].dest_id)
816       continue;
817     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
818       diff = curtime - conn->ping[i].start_time;
819       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
820           "Ping reply from %s: %d second%s", 
821           conn->ping[i].dest_name, diff, 
822           diff == 1 ? "" : "s");
823       
824       conn->ping[i].start_time = 0;
825       silc_free(conn->ping[i].dest_id);
826       conn->ping[i].dest_id = NULL;
827       silc_free(conn->ping[i].dest_name);
828       conn->ping[i].dest_name = NULL;
829       break;
830     }
831   }
832
833   silc_free(id);
834
835   /* Notify application */
836   COMMAND_REPLY((ARGS));
837
838  out:
839   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
840   silc_client_command_reply_free(cmd);
841 }
842
843 /* Received reply for JOIN command. */
844
845 SILC_CLIENT_CMD_REPLY_FUNC(join)
846 {
847   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
848   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
849   SilcChannelEntry channel;
850   SilcChannelUser chu;
851   SilcChannelID *channel_id;
852   SilcUInt32 argc, mode = 0, len, list_count;
853   char *topic, *tmp, *channel_name = NULL, *hmac;
854   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
855   int i;
856
857   SILC_LOG_DEBUG(("Start"));
858
859   if (cmd->error != SILC_STATUS_OK) {
860     if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
861       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
862           "%s", silc_get_status_message(cmd->error));
863     COMMAND_REPLY_ERROR;
864     goto out;
865   }
866
867   argc = silc_argument_get_arg_num(cmd->args);
868   if (argc < 7 || argc > 14) {
869     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
870         "Cannot join channel: Bad reply packet");
871     COMMAND_REPLY_ERROR;
872     goto out;
873   }
874
875   /* Get channel name */
876   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
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_name = tmp;
884
885   /* Get Channel ID */
886   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
887   if (!tmp) {
888     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
889         "Cannot join channel: Bad reply packet");
890     COMMAND_REPLY_ERROR;
891     goto out;
892   }
893   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
894   if (!channel_id) {
895     COMMAND_REPLY_ERROR;
896     goto out;
897   }
898
899   /* Get channel mode */
900   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
901   if (tmp)
902     SILC_GET32_MSB(mode, tmp);
903
904   /* Get channel key */
905   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
906   if (tmp) {
907     keyp = silc_buffer_alloc(len);
908     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
909     silc_buffer_put(keyp, tmp, len);
910   }
911
912   /* Get topic */
913   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
914
915   /* Check whether we have this channel entry already. */
916   channel = silc_client_get_channel(cmd->client, conn, channel_name);
917   if (channel) {
918     if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
919       silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
920   } else {
921     /* Create new channel entry */
922     channel = silc_client_add_channel(cmd->client, conn, channel_name, 
923                                       mode, channel_id);
924   }
925
926   conn->current_channel = channel;
927
928   /* Get hmac */
929   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
930   if (hmac) {
931     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
932       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
933           "Cannot join channel: Unsupported HMAC `%s'", hmac);
934       COMMAND_REPLY_ERROR;
935       goto out;
936     }
937   }
938
939   /* Get the list count */
940   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
941   if (!tmp)
942     goto out;
943   SILC_GET32_MSB(list_count, tmp);
944
945   /* Get Client ID list */
946   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
947   if (!tmp)
948     goto out;
949
950   client_id_list = silc_buffer_alloc(len);
951   silc_buffer_pull_tail(client_id_list, len);
952   silc_buffer_put(client_id_list, tmp, len);
953
954   /* Get client mode list */
955   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
956   if (!tmp)
957     goto out;
958
959   client_mode_list = silc_buffer_alloc(len);
960   silc_buffer_pull_tail(client_mode_list, len);
961   silc_buffer_put(client_mode_list, tmp, len);
962
963   /* Add clients we received in the reply to the channel */
964   for (i = 0; i < list_count; i++) {
965     SilcUInt16 idp_len;
966     SilcUInt32 mode;
967     SilcClientID *client_id;
968     SilcClientEntry client_entry;
969
970     /* Client ID */
971     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
972     idp_len += 4;
973     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
974     if (!client_id)
975       continue;
976
977     /* Mode */
978     SILC_GET32_MSB(mode, client_mode_list->data);
979
980     /* Check if we have this client cached already. */
981     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
982     if (!client_entry) {
983       /* No, we don't have it, add entry for it. */
984       client_entry = 
985         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
986                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
987     }
988
989     /* Join client to the channel */
990     if (!silc_client_on_channel(channel, client_entry)) {
991       chu = silc_calloc(1, sizeof(*chu));
992       chu->client = client_entry;
993       chu->channel = channel;
994       chu->mode = mode;
995       silc_hash_table_add(channel->user_list, client_entry, chu);
996       silc_hash_table_add(client_entry->channels, channel, chu);
997     }
998
999     silc_free(client_id);
1000     silc_buffer_pull(client_id_list, idp_len);
1001     silc_buffer_pull(client_mode_list, 4);
1002   }
1003   silc_buffer_push(client_id_list, client_id_list->data - 
1004                    client_id_list->head);
1005   silc_buffer_push(client_mode_list, client_mode_list->data - 
1006                    client_mode_list->head);
1007
1008   /* Save channel key */
1009   if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1010     silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1011
1012   /* Notify application */
1013   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1014                  keyp ? keyp->head : NULL, NULL,
1015                  NULL, topic, hmac, list_count, client_id_list, 
1016                  client_mode_list));
1017
1018  out:
1019   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1020   silc_client_command_reply_free(cmd);
1021
1022   if (keyp)
1023     silc_buffer_free(keyp);
1024   if (client_id_list)
1025     silc_buffer_free(client_id_list);
1026   if (client_mode_list)
1027     silc_buffer_free(client_mode_list);
1028 }
1029
1030 /* Received reply for MOTD command */
1031
1032 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1033 {
1034   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1035   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1036   SilcUInt32 argc, i;
1037   char *motd = NULL, *cp, line[256];
1038
1039   if (cmd->error != SILC_STATUS_OK) {
1040     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1041         "%s", silc_get_status_message(cmd->error));
1042     COMMAND_REPLY_ERROR;
1043     return;
1044   }
1045
1046   argc = silc_argument_get_arg_num(cmd->args);
1047   if (argc > 3) {
1048     COMMAND_REPLY_ERROR;
1049     goto out;
1050   }
1051
1052   if (argc == 3) {
1053     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1054     if (!motd) {
1055       COMMAND_REPLY_ERROR;
1056       goto out;
1057     }
1058
1059     i = 0;
1060     cp = motd;
1061     while(cp[i] != 0) {
1062       if (cp[i++] == '\n') {
1063         memset(line, 0, sizeof(line));
1064         strncat(line, cp, i - 1);
1065         cp += i;
1066         
1067         if (i == 2)
1068           line[0] = ' ';
1069         
1070         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1071         
1072         if (!strlen(cp))
1073           break;
1074         i = 0;
1075       }
1076     }
1077   }
1078
1079   /* Notify application */
1080   COMMAND_REPLY((ARGS, motd));
1081
1082  out:
1083   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1084   silc_client_command_reply_free(cmd);
1085 }
1086
1087 /* Received reply tot he UMODE command. Save the current user mode */
1088
1089 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1090 {
1091   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1092   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1093   unsigned char *tmp;
1094   SilcUInt32 mode;
1095
1096   if (cmd->error != SILC_STATUS_OK) {
1097     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1098         "%s", silc_get_status_message(cmd->error));
1099     COMMAND_REPLY_ERROR;
1100     goto out;
1101   }
1102
1103   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1104   if (!tmp) {
1105     COMMAND_REPLY_ERROR;
1106     goto out;
1107   }
1108
1109   SILC_GET32_MSB(mode, tmp);
1110   conn->local_entry->mode = mode;
1111
1112   /* Notify application */
1113   COMMAND_REPLY((ARGS, mode));
1114
1115  out:
1116   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1117   silc_client_command_reply_free(cmd);
1118 }
1119
1120 /* Received reply for CMODE command. */
1121
1122 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1123 {
1124   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1125   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1126   unsigned char *tmp;
1127   SilcUInt32 mode;
1128   SilcChannelID *channel_id;
1129   SilcChannelEntry channel;
1130   SilcUInt32 len;
1131
1132   if (cmd->error != SILC_STATUS_OK) {
1133     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1134         "%s", silc_get_status_message(cmd->error));
1135     COMMAND_REPLY_ERROR;
1136     goto out;
1137   }
1138
1139   /* Take Channel ID */
1140   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1141   if (!tmp)
1142     goto out;
1143   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1144   if (!channel_id)
1145     goto out;
1146
1147   /* Get the channel entry */
1148   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1149   if (!channel) {
1150     silc_free(channel_id);
1151     COMMAND_REPLY_ERROR;
1152     goto out;
1153   }
1154   
1155   /* Get channel mode */
1156   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1157   if (!tmp) {
1158     silc_free(channel_id);
1159     COMMAND_REPLY_ERROR;
1160     goto out;
1161   }
1162
1163   /* Save the mode */
1164   SILC_GET32_MSB(mode, tmp);
1165   channel->mode = mode;
1166
1167   /* Notify application */
1168   COMMAND_REPLY((ARGS, channel, mode));
1169
1170   silc_free(channel_id);
1171
1172  out:
1173   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1174   silc_client_command_reply_free(cmd);
1175 }
1176
1177 /* Received reply for CUMODE command */
1178
1179 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1180 {
1181   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1182   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1183   SilcClientID *client_id;
1184   SilcChannelID *channel_id;
1185   SilcClientEntry client_entry;
1186   SilcChannelEntry channel;
1187   SilcChannelUser chu;
1188   unsigned char *modev, *tmp, *id;
1189   SilcUInt32 len, mode;
1190   
1191   if (cmd->error != SILC_STATUS_OK) {
1192     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1193         "%s", silc_get_status_message(cmd->error));
1194     COMMAND_REPLY_ERROR;
1195     goto out;
1196   }
1197   
1198   /* Get channel mode */
1199   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1200   if (!modev) {
1201     COMMAND_REPLY_ERROR;
1202     goto out;
1203   }
1204
1205   /* Take Channel ID */
1206   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1207   if (!tmp)
1208     goto out;
1209   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1210   if (!channel_id)
1211     goto out;
1212
1213   /* Get the channel entry */
1214   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1215   if (!channel) {
1216     silc_free(channel_id);
1217     COMMAND_REPLY_ERROR;
1218     goto out;
1219   }
1220   
1221   /* Get Client ID */
1222   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1223   if (!id) {
1224     silc_free(channel_id);
1225     COMMAND_REPLY_ERROR;
1226     goto out;
1227   }
1228   client_id = silc_id_payload_parse_id(id, len, NULL);
1229   if (!client_id) {
1230     silc_free(channel_id);
1231     COMMAND_REPLY_ERROR;
1232     goto out;
1233   }
1234   
1235   /* Get client entry */
1236   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1237   if (!client_entry) {
1238     silc_free(channel_id);
1239     silc_free(client_id);
1240     COMMAND_REPLY_ERROR;
1241     goto out;
1242   }
1243
1244   /* Save the mode */
1245   SILC_GET32_MSB(mode, modev);
1246   chu = silc_client_on_channel(channel, client_entry);
1247   if (chu)
1248     chu->mode = mode;
1249
1250   /* Notify application */
1251   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1252   silc_free(client_id);
1253   silc_free(channel_id);
1254   
1255  out:
1256   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1257   silc_client_command_reply_free(cmd);
1258 }
1259
1260 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1261 {
1262   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1263   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1264
1265   if (cmd->error != SILC_STATUS_OK) {
1266     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1267         "%s", silc_get_status_message(cmd->error));
1268     COMMAND_REPLY_ERROR;
1269     goto out;
1270   }
1271
1272   /* Notify application */
1273   COMMAND_REPLY((ARGS));
1274
1275  out:
1276   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1277   silc_client_command_reply_free(cmd);
1278 }
1279
1280 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1281 {
1282   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1283   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1284
1285   if (cmd->error != SILC_STATUS_OK) {
1286     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1287         "%s", silc_get_status_message(cmd->error));
1288     COMMAND_REPLY_ERROR;
1289     goto out;
1290   }
1291
1292   /* Notify application */
1293   COMMAND_REPLY((ARGS));
1294
1295  out:
1296   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1297   silc_client_command_reply_free(cmd);
1298 }
1299
1300 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1301 {
1302   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1303   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1304
1305   if (cmd->error != SILC_STATUS_OK) {
1306     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1307         "%s", silc_get_status_message(cmd->error));
1308     COMMAND_REPLY_ERROR;
1309     goto out;
1310   }
1311
1312   /* Notify application */
1313   COMMAND_REPLY((ARGS));
1314
1315  out:
1316   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1317   silc_client_command_reply_free(cmd);
1318 }
1319
1320 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1321 {
1322   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1323   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1324   SilcBuffer detach;
1325
1326   if (cmd->error != SILC_STATUS_OK) {
1327     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1328         "%s", silc_get_status_message(cmd->error));
1329     COMMAND_REPLY_ERROR;
1330     goto out;
1331   }
1332
1333   /* Notify application */
1334   COMMAND_REPLY((ARGS));
1335
1336   /* Generate the detachment data and deliver it to the client in the
1337      detach client operation */
1338   detach = silc_client_get_detach_data(cmd->client, conn);
1339   if (detach) {
1340     cmd->client->internal->ops->detach(cmd->client, conn, 
1341                                        detach->data, detach->len);
1342     silc_buffer_free(detach);
1343   }
1344
1345  out:
1346   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1347   silc_client_command_reply_free(cmd);
1348 }
1349
1350 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1351 {
1352   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1353   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1354
1355   if (cmd->error != SILC_STATUS_OK) {
1356     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1357         "%s", silc_get_status_message(cmd->error));
1358     COMMAND_REPLY_ERROR;
1359     goto out;
1360   }
1361
1362   /* Notify application */
1363   COMMAND_REPLY((ARGS));
1364
1365  out:
1366   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1367   silc_client_command_reply_free(cmd);
1368 }
1369
1370 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1371 {
1372   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1373   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1374   SilcChannelEntry channel;
1375   SilcChannelID *channel_id;
1376   unsigned char *tmp;
1377   SilcUInt32 len;
1378
1379   if (cmd->error != SILC_STATUS_OK) {
1380     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1381         "%s", silc_get_status_message(cmd->error));
1382     COMMAND_REPLY_ERROR;
1383     goto out;
1384   }
1385
1386   /* Take Channel ID */
1387   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1388   if (!tmp)
1389     goto out;
1390
1391   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1392   if (!channel_id)
1393     goto out;
1394
1395   /* Get the channel entry */
1396   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1397   if (!channel) {
1398     silc_free(channel_id);
1399     COMMAND_REPLY_ERROR;
1400     goto out;
1401   }
1402   
1403   /* Get the ban list */
1404   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1405
1406   /* Notify application */
1407   COMMAND_REPLY((ARGS, channel, tmp));
1408
1409  out:
1410   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1411   silc_client_command_reply_free(cmd);
1412 }
1413
1414 /* Reply to LEAVE command. */
1415
1416 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1417 {
1418   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1419   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1420   SilcChannelID *channel_id;
1421   SilcChannelEntry channel = NULL;
1422   unsigned char *tmp;
1423   SilcUInt32 len;
1424
1425   if (cmd->error != SILC_STATUS_OK) {
1426     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1427         "%s", silc_get_status_message(cmd->error));
1428     COMMAND_REPLY_ERROR;
1429     goto out;
1430   }
1431
1432   /* From protocol version 1.1 we get the channel ID of the left channel */
1433   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1434   if (tmp) {
1435     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1436     if (!channel_id)
1437       goto out;
1438
1439     /* Get the channel entry */
1440     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1441     if (!channel) {
1442       silc_free(channel_id);
1443       COMMAND_REPLY_ERROR;
1444       goto out;
1445     }
1446
1447     silc_free(channel_id);
1448   }
1449
1450   /* Notify application */
1451   COMMAND_REPLY((ARGS, channel));
1452
1453  out:
1454   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1455   silc_client_command_reply_free(cmd);
1456 }
1457
1458 /* Channel resolving callback for USERS command reply. */
1459
1460 static void silc_client_command_reply_users_cb(SilcClient client,
1461                                                SilcClientConnection conn,
1462                                                SilcChannelEntry *channels,
1463                                                SilcUInt32 channels_count,
1464                                                void *context)
1465 {
1466   if (!channels_count) {
1467     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1468     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1469
1470     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1471     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1472         "%s", silc_get_status_message(cmd->error));
1473     COMMAND_REPLY_ERROR;
1474     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1475     silc_client_command_reply_free(cmd);
1476     return;
1477   }
1478
1479   silc_client_command_reply_users(context, NULL);
1480 }
1481
1482 static int
1483 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1484                                      SilcStatus status,
1485                                      bool notify,
1486                                      SilcGetChannelCallback get_channel,
1487                                      SilcCommandCb get_clients)
1488 {
1489   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1490   SilcChannelEntry channel;
1491   SilcClientEntry client_entry;
1492   SilcChannelUser chu;
1493   SilcChannelID *channel_id = NULL;
1494   SilcBufferStruct client_id_list, client_mode_list;
1495   unsigned char *tmp;
1496   SilcUInt32 tmp_len, list_count;
1497   int i;
1498   unsigned char **res_argv = NULL;
1499   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1500   bool wait_res = FALSE;
1501
1502   SILC_LOG_DEBUG(("Start"));
1503
1504   /* Get channel ID */
1505   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1506   if (!tmp) {
1507     COMMAND_REPLY_ERROR;
1508     goto out;
1509   }
1510   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1511   if (!channel_id) {
1512     COMMAND_REPLY_ERROR;
1513     goto out;
1514   }
1515   
1516   /* Get the list count */
1517   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1518   if (!tmp) {
1519     COMMAND_REPLY_ERROR;
1520     goto out;
1521   }
1522   SILC_GET32_MSB(list_count, tmp);
1523
1524   /* Get Client ID list */
1525   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1526   if (!tmp) {
1527     COMMAND_REPLY_ERROR;
1528     goto out;
1529   }
1530   silc_buffer_set(&client_id_list, tmp, tmp_len);
1531
1532   /* Get client mode list */
1533   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1534   if (!tmp) {
1535     COMMAND_REPLY_ERROR;
1536     goto out;
1537   }
1538   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1539
1540   /* Get channel entry */
1541   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1542   if (!channel) {
1543     /* Resolve the channel from server */
1544     silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1545                                           get_channel, cmd);
1546     silc_free(channel_id);
1547     return 1;
1548   }
1549
1550   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1551
1552   /* Cache the received Client ID's and modes. */
1553   for (i = 0; i < list_count; i++) {
1554     SilcUInt16 idp_len;
1555     SilcUInt32 mode;
1556     SilcClientID *client_id;
1557
1558     /* Client ID */
1559     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1560     idp_len += 4;
1561     client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1562     if (!client_id)
1563       continue;
1564
1565     /* Mode */
1566     SILC_GET32_MSB(mode, client_mode_list.data);
1567
1568     /* Check if we have this client cached already. */
1569     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1570     if (!client_entry || !client_entry->username || !client_entry->realname) {
1571       if (client_entry) {
1572         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
1573           /* Attach to this resolving and wait until it finishes */
1574           silc_client_command_pending(conn, SILC_COMMAND_NONE, 
1575                                       client_entry->resolve_cmd_ident,
1576                                       get_clients, cmd);
1577           wait_res = TRUE;
1578
1579           silc_buffer_pull(&client_id_list, idp_len);
1580           silc_buffer_pull(&client_mode_list, 4);
1581           continue;
1582         }
1583         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
1584         client_entry->resolve_cmd_ident = conn->cmd_ident + 1;
1585       }
1586
1587       /* No we don't have it (or it is incomplete in information), query
1588          it from the server. Assemble argument table that will be sent
1589          for the WHOIS command later. */
1590       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1591                               (res_argc + 1));
1592       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1593                                    (res_argc + 1));
1594       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1595                                     (res_argc + 1));
1596       res_argv[res_argc] = client_id_list.data;
1597       res_argv_lens[res_argc] = idp_len;
1598       res_argv_types[res_argc] = res_argc + 3;
1599       res_argc++;
1600     } else {
1601       if (!silc_client_on_channel(channel, client_entry)) {
1602         chu = silc_calloc(1, sizeof(*chu));
1603         chu->client = client_entry;
1604         chu->mode = mode;
1605         chu->channel = channel;
1606         silc_hash_table_add(channel->user_list, client_entry, chu);
1607         silc_hash_table_add(client_entry->channels, channel, chu);
1608       }
1609     }
1610
1611     silc_free(client_id);
1612     silc_buffer_pull(&client_id_list, idp_len);
1613     silc_buffer_pull(&client_mode_list, 4);
1614   }
1615
1616   /* Query the client information from server if the list included clients
1617      that we don't know about. */
1618   if (res_argc) {
1619     SilcBuffer res_cmd;
1620
1621     /* Send the WHOIS command to server */
1622     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1623                                  silc_client_command_reply_whois_i, 0,
1624                                  ++conn->cmd_ident);
1625     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1626                                           res_argc, res_argv, res_argv_lens,
1627                                           res_argv_types, conn->cmd_ident);
1628     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1629                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1630                             TRUE);
1631
1632     /* Register pending command callback. After we've received the WHOIS
1633        command reply we will reprocess this command reply by re-calling this
1634        USERS command reply callback. */
1635     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1636                                 get_clients, cmd);
1637
1638     silc_buffer_free(res_cmd);
1639     silc_free(channel_id);
1640     silc_free(res_argv);
1641     silc_free(res_argv_lens);
1642     silc_free(res_argv_types);
1643     return 1;
1644   }
1645
1646   if (wait_res)
1647     return 1;
1648
1649   silc_buffer_push(&client_id_list, (client_id_list.data - 
1650                                      client_id_list.head));
1651   silc_buffer_push(&client_mode_list, (client_mode_list.data - 
1652                                        client_mode_list.head));
1653
1654   /* Notify application */
1655   if (notify)
1656     COMMAND_REPLY((ARGS, channel, list_count, &client_id_list, 
1657                    &client_mode_list));
1658
1659  out:
1660   silc_free(channel_id);
1661   return 0;
1662 }
1663
1664 /* Reply to USERS command. Received list of client ID's and theirs modes
1665    on the channel we requested. */
1666
1667 SILC_CLIENT_CMD_REPLY_FUNC(users)
1668 {
1669   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1670   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1671
1672   SILC_LOG_DEBUG(("Start"));
1673
1674   if (cmd->error != SILC_STATUS_OK) {
1675     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1676         "%s", silc_get_status_message(cmd->error));
1677     COMMAND_REPLY_ERROR;
1678     goto out;
1679   }
1680
1681   if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
1682                                            silc_client_command_reply_users_cb,
1683                                            silc_client_command_reply_users))
1684     return;
1685
1686  out:
1687   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1688   silc_client_command_reply_free(cmd);
1689 }
1690
1691 /* Received command reply to GETKEY command. WE've received the remote
1692    client's public key. */
1693
1694 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1695 {
1696   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1697   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1698   SilcIDPayload idp = NULL;
1699   SilcClientID *client_id = NULL;
1700   SilcClientEntry client_entry;
1701   SilcServerID *server_id = NULL;
1702   SilcServerEntry server_entry;
1703   SilcSKEPKType type;
1704   unsigned char *tmp, *pk;
1705   SilcUInt32 len;
1706   SilcUInt16 pk_len;
1707   SilcIdType id_type;
1708   SilcPublicKey public_key = NULL;
1709
1710   SILC_LOG_DEBUG(("Start"));
1711
1712   if (cmd->error != SILC_STATUS_OK) {
1713     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1714         "%s", silc_get_status_message(cmd->error));
1715     COMMAND_REPLY_ERROR;
1716     goto out;
1717   }
1718
1719   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1720   if (!tmp) {
1721     COMMAND_REPLY_ERROR;
1722     goto out;
1723   }
1724   idp = silc_id_payload_parse(tmp, len);
1725   if (!idp) {
1726     COMMAND_REPLY_ERROR;
1727     goto out;
1728   }
1729
1730   /* Get the public key payload */
1731   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1732   if (tmp) {
1733     /* Decode the public key */
1734     SILC_GET16_MSB(pk_len, tmp);
1735     SILC_GET16_MSB(type, tmp + 2);
1736     pk = tmp + 4;
1737     
1738     if (type == SILC_SKE_PK_TYPE_SILC)
1739       if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1740         public_key = NULL;
1741   } 
1742    
1743   id_type = silc_id_payload_get_type(idp);
1744   if (id_type == SILC_ID_CLIENT) {
1745     /* Received client's public key */
1746     client_id = silc_id_payload_get_id(idp);
1747     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1748     if (!client_entry) {
1749       COMMAND_REPLY_ERROR;
1750       goto out;
1751     }
1752
1753     /* Notify application */
1754     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1755   } else if (id_type == SILC_ID_SERVER) {
1756     /* Received server's public key */
1757     server_id = silc_id_payload_get_id(idp);
1758     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1759     if (!server_entry) {
1760       COMMAND_REPLY_ERROR;
1761       goto out;
1762     }
1763
1764     /* Notify application */
1765     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1766   }
1767
1768  out:
1769   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1770   if (idp)
1771     silc_id_payload_free(idp);
1772   if (public_key)
1773     silc_pkcs_public_key_free(public_key);
1774   silc_free(client_id);
1775   silc_free(server_id);
1776   silc_client_command_reply_free(cmd);
1777 }
1778
1779 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1780 {
1781   silc_client_command_reply_free(context);
1782 }
1783
1784
1785 /******************************************************************************
1786
1787                       Internal command reply functions
1788
1789 ******************************************************************************/
1790
1791 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1792 {
1793   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1794   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1795
1796   SILC_LOG_DEBUG(("Start"));
1797
1798   if (cmd->error != SILC_STATUS_OK)
1799     goto out;
1800
1801   /* Save WHOIS info */
1802   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1803
1804   /* Pending callbacks are not executed if this was an list entry */
1805   if (cmd->status != SILC_STATUS_OK &&
1806       cmd->status != SILC_STATUS_LIST_END) {
1807     silc_client_command_reply_free(cmd);
1808     return;
1809   }
1810
1811  out:
1812   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1813
1814   /* If we received notify for invalid ID we'll remove the ID if we
1815      have it cached. */
1816   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1817     SilcClientEntry client_entry;
1818     SilcUInt32 tmp_len;
1819     unsigned char *tmp =
1820       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1821                                  2, &tmp_len);
1822     if (tmp) {
1823       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1824       if (client_id) {
1825         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1826                                                     client_id);
1827         if (client_entry)
1828           silc_client_del_client(cmd->client, conn, client_entry);
1829         silc_free(client_id);
1830       }
1831     }
1832   }
1833
1834   /* Unregister this command reply */
1835   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1836                                  NULL, silc_client_command_reply_whois_i,
1837                                  cmd->ident);
1838
1839   silc_client_command_reply_free(cmd);
1840 }
1841
1842 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1843 {
1844   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1845   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1846
1847   SILC_LOG_DEBUG(("Start"));
1848
1849   if (cmd->error != SILC_STATUS_OK)
1850     goto out;
1851
1852   /* Save IDENTIFY info */
1853   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1854
1855   /* Pending callbacks are not executed if this was an list entry */
1856   if (cmd->status != SILC_STATUS_OK &&
1857       cmd->status != SILC_STATUS_LIST_END) {
1858     silc_client_command_reply_free(cmd);
1859     return;
1860   }
1861
1862  out:
1863   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1864
1865   /* If we received notify for invalid ID we'll remove the ID if we
1866      have it cached. */
1867   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1868     SilcClientEntry client_entry;
1869     SilcUInt32 tmp_len;
1870     unsigned char *tmp =
1871       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1872                                  2, &tmp_len);
1873     if (tmp) {
1874       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1875       if (client_id) {
1876         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1877                                                     client_id);
1878         if (client_entry)
1879           silc_client_del_client(cmd->client, conn, client_entry);
1880         silc_free(client_id);
1881       }
1882     }
1883   }
1884
1885   /* Unregister this command reply */
1886   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1887                                  NULL, silc_client_command_reply_identify_i,
1888                                  cmd->ident);
1889
1890   silc_client_command_reply_free(cmd);
1891 }
1892
1893 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1894 {
1895   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1896   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1897   unsigned char *tmp;
1898   SilcServerEntry server;
1899   SilcServerID *server_id = NULL;
1900   char *server_name, *server_info;
1901   SilcUInt32 len;
1902
1903   SILC_LOG_DEBUG(("Start"));
1904
1905   if (cmd->error != SILC_STATUS_OK)
1906     goto out;
1907
1908   /* Get server ID */
1909   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1910   if (!tmp)
1911     goto out;
1912
1913   server_id = silc_id_payload_parse_id(tmp, len, NULL);
1914   if (!server_id)
1915     goto out;
1916
1917   /* Get server name */
1918   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1919   if (!server_name)
1920     goto out;
1921
1922   /* Get server info */
1923   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
1924   if (!server_info)
1925     goto out;
1926
1927   /* See whether we have this server cached. If not create it. */
1928   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
1929   if (!server) {
1930     SILC_LOG_DEBUG(("New server entry"));
1931     silc_client_add_server(cmd->client, conn, server_name, server_info,
1932                            silc_id_dup(server_id, SILC_ID_SERVER));
1933   }
1934   
1935  out:
1936   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
1937   silc_free(server_id);
1938   silc_client_command_reply_free(cmd);
1939 }
1940
1941 static void silc_client_command_reply_users_i_cb(SilcClient client,
1942                                                  SilcClientConnection conn,
1943                                                  SilcChannelEntry *channels,
1944                                                  SilcUInt32 channels_count,
1945                                                  void *context)
1946 {
1947   if (!channels_count) {
1948     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1949     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1950
1951     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1952     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1953         "%s", silc_get_status_message(cmd->error));
1954     COMMAND_REPLY_ERROR;
1955     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1956     silc_client_command_reply_free(cmd);
1957     return;
1958   }
1959
1960   silc_client_command_reply_users_i(context, NULL);
1961 }
1962
1963 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
1964 {
1965   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1966
1967   SILC_LOG_DEBUG(("Start"));
1968
1969   if (cmd->error != SILC_STATUS_OK)
1970     goto out;
1971
1972   /* Save USERS info */
1973   if (silc_client_command_reply_users_save(
1974                                     cmd, cmd->status, FALSE,
1975                                     silc_client_command_reply_users_i_cb,
1976                                     silc_client_command_reply_users_i))
1977     return;
1978
1979  out:
1980   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1981
1982   /* Unregister this command reply */
1983   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
1984                                  NULL, silc_client_command_reply_users_i,
1985                                  cmd->ident);
1986
1987   silc_client_command_reply_free(cmd);
1988 }
1989
1990 /* Private range commands, specific to this implementation (and compatible
1991    with SILC Server >= 0.9). */
1992
1993 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1994 {
1995   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1996   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1997
1998   if (cmd->error != SILC_STATUS_OK) {
1999     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2000         "%s", silc_get_status_message(cmd->error));
2001     COMMAND_REPLY_ERROR;
2002     goto out;
2003   }
2004
2005   /* Notify application */
2006   COMMAND_REPLY((ARGS));
2007
2008  out:
2009   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2010   silc_client_command_reply_free(cmd);
2011 }
2012
2013 SILC_CLIENT_CMD_REPLY_FUNC(close)
2014 {
2015   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2016   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2017
2018   if (cmd->error != SILC_STATUS_OK) {
2019     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2020         "%s", silc_get_status_message(cmd->error));
2021     COMMAND_REPLY_ERROR;
2022     goto out;
2023   }
2024
2025   /* Notify application */
2026   COMMAND_REPLY((ARGS));
2027
2028  out:
2029   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2030   silc_client_command_reply_free(cmd);
2031 }
2032  
2033 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2034 {
2035   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2036   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2037
2038   if (cmd->error != SILC_STATUS_OK) {
2039     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2040         "%s", silc_get_status_message(cmd->error));
2041     COMMAND_REPLY_ERROR;
2042     goto out;
2043   }
2044
2045   /* Notify application */
2046   COMMAND_REPLY((ARGS));
2047
2048  out:
2049   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2050   silc_client_command_reply_free(cmd);
2051 }