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