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