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