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