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