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