Added support for UTF-8 encoded nicks, channel names and
[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
1053   conn->current_channel = channel;
1054   channel->mode = mode;
1055
1056   /* Get hmac */
1057   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1058   if (hmac) {
1059     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1060       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1061           "Cannot join channel: Unsupported HMAC `%s'", hmac);
1062       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1063       goto out;
1064     }
1065   }
1066
1067   /* Get the list count */
1068   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1069   if (!tmp)
1070     goto out;
1071   SILC_GET32_MSB(list_count, tmp);
1072
1073   /* Get Client ID list */
1074   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1075   if (!tmp)
1076     goto out;
1077
1078   client_id_list = silc_buffer_alloc(len);
1079   silc_buffer_pull_tail(client_id_list, len);
1080   silc_buffer_put(client_id_list, tmp, len);
1081
1082   /* Get client mode list */
1083   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1084   if (!tmp)
1085     goto out;
1086
1087   client_mode_list = silc_buffer_alloc(len);
1088   silc_buffer_pull_tail(client_mode_list, len);
1089   silc_buffer_put(client_mode_list, tmp, len);
1090
1091   /* Add clients we received in the reply to the channel */
1092   for (i = 0; i < list_count; i++) {
1093     SilcUInt16 idp_len;
1094     SilcUInt32 mode;
1095     SilcClientID *client_id;
1096     SilcClientEntry client_entry;
1097
1098     /* Client ID */
1099     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1100     idp_len += 4;
1101     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1102     if (!client_id)
1103       continue;
1104
1105     /* Mode */
1106     SILC_GET32_MSB(mode, client_mode_list->data);
1107
1108     /* Check if we have this client cached already. */
1109     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1110     if (!client_entry) {
1111       /* No, we don't have it, add entry for it. */
1112       client_entry =
1113         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1114                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1115     }
1116
1117     /* Join client to the channel */
1118     if (!silc_client_on_channel(channel, client_entry)) {
1119       chu = silc_calloc(1, sizeof(*chu));
1120       chu->client = client_entry;
1121       chu->channel = channel;
1122       chu->mode = mode;
1123       silc_hash_table_add(channel->user_list, client_entry, chu);
1124       silc_hash_table_add(client_entry->channels, channel, chu);
1125     }
1126
1127     silc_free(client_id);
1128     silc_buffer_pull(client_id_list, idp_len);
1129     silc_buffer_pull(client_mode_list, 4);
1130   }
1131   silc_buffer_push(client_id_list, client_id_list->data -
1132                    client_id_list->head);
1133   silc_buffer_push(client_mode_list, client_mode_list->data -
1134                    client_mode_list->head);
1135
1136   /* Save channel key */
1137   if (keyp)
1138     silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1139
1140   /* Get founder key */
1141   tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
1142   if (tmp) {
1143     if (channel->founder_key)
1144       silc_pkcs_public_key_free(channel->founder_key);
1145     channel->founder_key = NULL;
1146     silc_pkcs_public_key_payload_decode(tmp, len, &channel->founder_key);
1147   }
1148
1149   /* Get user limit */
1150   tmp = silc_argument_get_arg_type(cmd->args, 17, &len);
1151   if (tmp && len == 4)
1152     SILC_GET32_MSB(channel->user_limit, tmp);
1153   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1154     channel->user_limit = 0;
1155
1156   /* Get channel public key list */
1157   tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
1158   if (tmp)
1159     silc_buffer_set(&chpklist, tmp, len);
1160
1161   if (topic) {
1162     silc_free(channel->topic);
1163     channel->topic = silc_memdup(topic, strlen(topic));
1164   }
1165
1166   /* Notify application */
1167   COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
1168                  keyp ? keyp->head : NULL, NULL,
1169                  NULL, topic, hmac, list_count, client_id_list,
1170                  client_mode_list, channel->founder_key,
1171                  tmp ? &chpklist : NULL, channel->user_limit));
1172
1173  out:
1174   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1175   silc_client_command_reply_free(cmd);
1176   silc_buffer_free(keyp);
1177   silc_buffer_free(client_id_list);
1178   silc_buffer_free(client_mode_list);
1179 }
1180
1181 /* Received reply for MOTD command */
1182
1183 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1184 {
1185   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1186   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1187   SilcUInt32 argc, i;
1188   char *motd = NULL, *cp, line[256];
1189
1190   if (cmd->error != SILC_STATUS_OK) {
1191     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1192         "%s", silc_get_status_message(cmd->error));
1193     COMMAND_REPLY_ERROR(cmd->error);
1194     return;
1195   }
1196
1197   argc = silc_argument_get_arg_num(cmd->args);
1198   if (argc > 3) {
1199     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1200     goto out;
1201   }
1202
1203   if (argc == 3) {
1204     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1205     if (!motd) {
1206       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1207       goto out;
1208     }
1209
1210     i = 0;
1211     cp = motd;
1212     while(cp[i] != 0) {
1213       if (cp[i++] == '\n') {
1214         memset(line, 0, sizeof(line));
1215         silc_strncat(line, sizeof(line), cp, i - 1);
1216         cp += i;
1217
1218         if (i == 2)
1219           line[0] = ' ';
1220
1221         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1222
1223         if (!strlen(cp))
1224           break;
1225         i = 0;
1226       }
1227     }
1228   }
1229
1230   /* Notify application */
1231   COMMAND_REPLY((SILC_ARGS, motd));
1232
1233  out:
1234   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1235   silc_client_command_reply_free(cmd);
1236 }
1237
1238 /* Received reply tot he UMODE command. Save the current user mode */
1239
1240 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1241 {
1242   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1243   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1244   unsigned char *tmp;
1245   SilcUInt32 mode;
1246
1247   if (cmd->error != SILC_STATUS_OK) {
1248     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1249         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1250     COMMAND_REPLY_ERROR(cmd->error);
1251     goto out;
1252   }
1253
1254   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1255   if (!tmp) {
1256     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1257     goto out;
1258   }
1259
1260   SILC_GET32_MSB(mode, tmp);
1261   conn->local_entry->mode = mode;
1262
1263   /* Notify application */
1264   COMMAND_REPLY((SILC_ARGS, mode));
1265
1266  out:
1267   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1268   silc_client_command_reply_free(cmd);
1269 }
1270
1271 /* Received reply for CMODE command. */
1272
1273 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1274 {
1275   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1276   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1277   unsigned char *tmp;
1278   SilcUInt32 mode;
1279   SilcChannelID *channel_id;
1280   SilcChannelEntry channel;
1281   SilcUInt32 len;
1282   SilcPublicKey public_key = NULL;
1283   SilcBufferStruct channel_pubkeys;
1284
1285   if (cmd->error != SILC_STATUS_OK) {
1286     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1287         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1288     COMMAND_REPLY_ERROR(cmd->error);
1289     goto out;
1290   }
1291
1292   /* Take Channel ID */
1293   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1294   if (!tmp)
1295     goto out;
1296   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1297   if (!channel_id)
1298     goto out;
1299
1300   /* Get the channel entry */
1301   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1302   if (!channel) {
1303     silc_free(channel_id);
1304     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1305     goto out;
1306   }
1307
1308   /* Get channel mode */
1309   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1310   if (!tmp) {
1311     silc_free(channel_id);
1312     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1313     goto out;
1314   }
1315
1316   /* Save the mode */
1317   SILC_GET32_MSB(mode, tmp);
1318   channel->mode = mode;
1319
1320   /* Get founder public key */
1321   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1322   if (tmp) {
1323     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1324       public_key = NULL;
1325   }
1326
1327   /* Get user limit */
1328   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
1329   if (tmp && len == 4)
1330     SILC_GET32_MSB(channel->user_limit, tmp);
1331   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1332     channel->user_limit = 0;
1333
1334   /* Get channel public key(s) */
1335   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1336   if (tmp)
1337     silc_buffer_set(&channel_pubkeys, tmp, len);
1338
1339   /* Notify application */
1340   COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1341                  tmp ? &channel_pubkeys : NULL, channel->user_limit));
1342
1343   silc_free(channel_id);
1344
1345  out:
1346   if (public_key)
1347     silc_pkcs_public_key_free(public_key);
1348   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1349   silc_client_command_reply_free(cmd);
1350 }
1351
1352 /* Received reply for CUMODE command */
1353
1354 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1355 {
1356   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1357   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1358   SilcClientID *client_id;
1359   SilcChannelID *channel_id;
1360   SilcClientEntry client_entry;
1361   SilcChannelEntry channel;
1362   SilcChannelUser chu;
1363   unsigned char *modev, *tmp, *id;
1364   SilcUInt32 len, mode;
1365
1366   if (cmd->error != SILC_STATUS_OK) {
1367     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1368         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1369     COMMAND_REPLY_ERROR(cmd->error);
1370     goto out;
1371   }
1372
1373   /* Get channel mode */
1374   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1375   if (!modev) {
1376     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1377     goto out;
1378   }
1379
1380   /* Take Channel ID */
1381   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1382   if (!tmp)
1383     goto out;
1384   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1385   if (!channel_id)
1386     goto out;
1387
1388   /* Get the channel entry */
1389   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1390   if (!channel) {
1391     silc_free(channel_id);
1392     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1393     goto out;
1394   }
1395
1396   /* Get Client ID */
1397   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1398   if (!id) {
1399     silc_free(channel_id);
1400     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1401     goto out;
1402   }
1403   client_id = silc_id_payload_parse_id(id, len, NULL);
1404   if (!client_id) {
1405     silc_free(channel_id);
1406     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1407     goto out;
1408   }
1409
1410   /* Get client entry */
1411   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1412   if (!client_entry) {
1413     silc_free(channel_id);
1414     silc_free(client_id);
1415     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1416     goto out;
1417   }
1418
1419   /* Save the mode */
1420   SILC_GET32_MSB(mode, modev);
1421   chu = silc_client_on_channel(channel, client_entry);
1422   if (chu)
1423     chu->mode = mode;
1424
1425   /* Notify application */
1426   COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1427   silc_free(client_id);
1428   silc_free(channel_id);
1429
1430  out:
1431   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1432   silc_client_command_reply_free(cmd);
1433 }
1434
1435 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1436 {
1437   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1438   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1439   SilcClientID *client_id = NULL;
1440   SilcChannelID *channel_id = NULL;
1441   SilcClientEntry client_entry = NULL;
1442   SilcChannelEntry channel = NULL;
1443   unsigned char *tmp;
1444   SilcUInt32 len;
1445
1446   if (cmd->error != SILC_STATUS_OK) {
1447     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1448         "Cannot kick: %s", silc_get_status_message(cmd->error));
1449     COMMAND_REPLY_ERROR(cmd->error);
1450     goto out;
1451   }
1452
1453   /* Take Channel ID */
1454   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1455   if (tmp) {
1456     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1457     if (!channel_id) {
1458       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1459       goto out;
1460     }
1461
1462     /* Get the channel entry */
1463     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1464     if (!channel) {
1465       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1466       goto out;
1467     }
1468   }
1469
1470   /* Get Client ID */
1471   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1472   if (tmp) {
1473     client_id = silc_id_payload_parse_id(tmp, len, NULL);
1474     if (!client_id) {
1475       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1476       goto out;
1477     }
1478
1479     /* Get client entry */
1480     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1481     if (!client_entry) {
1482       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1483       goto out;
1484     }
1485   }
1486
1487   /* Notify application */
1488   COMMAND_REPLY((SILC_ARGS, channel, client_entry));
1489
1490  out:
1491   silc_free(channel_id);
1492   silc_free(client_id);
1493   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1494   silc_client_command_reply_free(cmd);
1495 }
1496
1497 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1498 {
1499   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1500   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1501
1502   if (cmd->error != SILC_STATUS_OK) {
1503     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1504         "%s", silc_get_status_message(cmd->error));
1505     COMMAND_REPLY_ERROR(cmd->error);
1506     goto out;
1507   }
1508
1509   /* Notify application */
1510   COMMAND_REPLY((SILC_ARGS));
1511
1512  out:
1513   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1514   silc_client_command_reply_free(cmd);
1515 }
1516
1517 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1518 {
1519   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1520   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1521
1522   if (cmd->error != SILC_STATUS_OK) {
1523     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1524         "%s", silc_get_status_message(cmd->error));
1525     COMMAND_REPLY_ERROR(cmd->error);
1526     goto out;
1527   }
1528
1529   /* Notify application */
1530   COMMAND_REPLY((SILC_ARGS));
1531
1532  out:
1533   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1534   silc_client_command_reply_free(cmd);
1535 }
1536
1537 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1538 {
1539   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1540   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1541   SilcBuffer detach;
1542
1543   if (cmd->error != SILC_STATUS_OK) {
1544     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1545         "%s", silc_get_status_message(cmd->error));
1546     COMMAND_REPLY_ERROR(cmd->error);
1547     goto out;
1548   }
1549
1550   /* Notify application */
1551   COMMAND_REPLY((SILC_ARGS));
1552
1553   /* Generate the detachment data and deliver it to the client in the
1554      detach client operation */
1555   detach = silc_client_get_detach_data(cmd->client, conn);
1556   if (detach) {
1557     cmd->client->internal->ops->detach(cmd->client, conn,
1558                                        detach->data, detach->len);
1559     silc_buffer_free(detach);
1560   }
1561
1562  out:
1563   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1564   silc_client_command_reply_free(cmd);
1565 }
1566
1567 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1568 {
1569   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1570   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1571
1572   if (cmd->error != SILC_STATUS_OK) {
1573     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1574         "%s", silc_get_status_message(cmd->error));
1575     COMMAND_REPLY_ERROR(cmd->error);
1576     goto out;
1577   }
1578
1579   /* Notify application */
1580   COMMAND_REPLY((SILC_ARGS));
1581
1582  out:
1583   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1584   silc_client_command_reply_free(cmd);
1585 }
1586
1587 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1588 {
1589   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1590   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1591   SilcChannelEntry channel;
1592   SilcChannelID *channel_id;
1593   unsigned char *tmp;
1594   SilcUInt32 len;
1595   SilcBufferStruct buf;
1596
1597   if (cmd->error != SILC_STATUS_OK) {
1598     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1599         "%s", silc_get_status_message(cmd->error));
1600     COMMAND_REPLY_ERROR(cmd->error);
1601     goto out;
1602   }
1603
1604   /* Take Channel ID */
1605   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1606   if (!tmp)
1607     goto out;
1608
1609   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1610   if (!channel_id)
1611     goto out;
1612
1613   /* Get the channel entry */
1614   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1615   if (!channel) {
1616     silc_free(channel_id);
1617     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1618     goto out;
1619   }
1620
1621   /* Get the ban list */
1622   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1623   if (tmp)
1624     silc_buffer_set(&buf, tmp, len);
1625
1626   /* Notify application */
1627   COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1628
1629  out:
1630   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1631   silc_client_command_reply_free(cmd);
1632 }
1633
1634 /* Reply to LEAVE command. */
1635
1636 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1637 {
1638   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1639   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1640   SilcChannelID *channel_id;
1641   SilcChannelEntry channel = NULL;
1642   SilcChannelUser chu;
1643   unsigned char *tmp;
1644   SilcUInt32 len;
1645
1646   if (cmd->error != SILC_STATUS_OK) {
1647     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1648         "%s", silc_get_status_message(cmd->error));
1649     COMMAND_REPLY_ERROR(cmd->error);
1650     goto out;
1651   }
1652
1653   /* From protocol version 1.1 we get the channel ID of the left channel */
1654   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1655   if (tmp) {
1656     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1657     if (!channel_id)
1658       goto out;
1659
1660     /* Get the channel entry */
1661     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1662     if (!channel) {
1663       silc_free(channel_id);
1664       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1665       goto out;
1666     }
1667
1668     /* Remove us from this channel. */
1669     chu = silc_client_on_channel(channel, conn->local_entry);
1670     if (chu) {
1671       silc_hash_table_del(chu->client->channels, chu->channel);
1672       silc_hash_table_del(chu->channel->user_list, chu->client);
1673       silc_free(chu);
1674     }
1675
1676     silc_free(channel_id);
1677   }
1678
1679   /* Notify application */
1680   COMMAND_REPLY((SILC_ARGS, channel));
1681
1682   /* Now delete the channel. */
1683   if (channel)
1684     silc_client_del_channel(cmd->client, conn, channel);
1685
1686  out:
1687   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1688   silc_client_command_reply_free(cmd);
1689 }
1690
1691 /* Channel resolving callback for USERS command reply. */
1692
1693 static void silc_client_command_reply_users_cb(SilcClient client,
1694                                                SilcClientConnection conn,
1695                                                SilcChannelEntry *channels,
1696                                                SilcUInt32 channels_count,
1697                                                void *context)
1698 {
1699   if (!channels_count) {
1700     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1701     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1702
1703     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1704     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1705         "%s", silc_get_status_message(cmd->error));
1706     COMMAND_REPLY_ERROR(cmd->error);
1707     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1708     silc_client_command_reply_free(cmd);
1709     return;
1710   }
1711
1712   silc_client_command_reply_users(context, NULL);
1713 }
1714
1715 static int
1716 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1717                                      SilcStatus status,
1718                                      bool notify,
1719                                      bool resolve,
1720                                      SilcGetChannelCallback get_channel,
1721                                      SilcCommandCb get_clients)
1722 {
1723   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1724   SilcChannelEntry channel;
1725   SilcClientEntry client_entry;
1726   SilcChannelUser chu;
1727   SilcChannelID *channel_id = NULL;
1728   SilcBufferStruct client_id_list, client_mode_list;
1729   unsigned char *tmp;
1730   SilcUInt32 tmp_len, list_count;
1731   int i;
1732   unsigned char **res_argv = NULL;
1733   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1734   bool wait_res = FALSE;
1735
1736   SILC_LOG_DEBUG(("Start"));
1737
1738   /* Get channel ID */
1739   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1740   if (!tmp) {
1741     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1742     goto out;
1743   }
1744   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1745   if (!channel_id) {
1746     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1747     goto out;
1748   }
1749
1750   /* Get the list count */
1751   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1752   if (!tmp) {
1753     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1754     goto out;
1755   }
1756   SILC_GET32_MSB(list_count, tmp);
1757
1758   /* Get Client ID list */
1759   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1760   if (!tmp) {
1761     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1762     goto out;
1763   }
1764   silc_buffer_set(&client_id_list, tmp, tmp_len);
1765
1766   /* Get client mode list */
1767   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1768   if (!tmp) {
1769     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1770     goto out;
1771   }
1772   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1773
1774   /* Get channel entry */
1775   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1776   if (!channel) {
1777     /* Resolve the channel from server */
1778     silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1779                                           get_channel, cmd);
1780     silc_free(channel_id);
1781     return 1;
1782   }
1783
1784   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1785
1786   /* Cache the received Client ID's and modes. */
1787   for (i = 0; i < list_count; i++) {
1788     SilcUInt16 idp_len;
1789     SilcUInt32 mode;
1790     SilcClientID *client_id;
1791
1792     /* Client ID */
1793     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1794     idp_len += 4;
1795     client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1796     if (!client_id)
1797       continue;
1798
1799     /* Mode */
1800     SILC_GET32_MSB(mode, client_mode_list.data);
1801
1802     /* Check if we have this client cached already. */
1803     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1804     if (!client_entry || !client_entry->username || !client_entry->realname) {
1805       if (resolve) {
1806         /* No we don't have it (or it is incomplete in information), query
1807            it from the server. Assemble argument table that will be sent
1808            for the WHOIS command later. */
1809         res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1810                                 (res_argc + 1));
1811         res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1812                                      (res_argc + 1));
1813         res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1814                                       (res_argc + 1));
1815         res_argv[res_argc] = client_id_list.data;
1816         res_argv_lens[res_argc] = idp_len;
1817         res_argv_types[res_argc] = res_argc + 4;
1818         res_argc++;
1819       }
1820     } else {
1821       if (!silc_client_on_channel(channel, client_entry)) {
1822         chu = silc_calloc(1, sizeof(*chu));
1823         chu->client = client_entry;
1824         chu->mode = mode;
1825         chu->channel = channel;
1826         silc_hash_table_add(channel->user_list, client_entry, chu);
1827         silc_hash_table_add(client_entry->channels, channel, chu);
1828       }
1829     }
1830
1831     silc_free(client_id);
1832     silc_buffer_pull(&client_id_list, idp_len);
1833     silc_buffer_pull(&client_mode_list, 4);
1834   }
1835
1836   /* Query the client information from server if the list included clients
1837      that we don't know about. */
1838   if (res_argc) {
1839     SilcBuffer res_cmd;
1840
1841     /* Send the WHOIS command to server */
1842     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1843                                  silc_client_command_reply_whois_i, 0,
1844                                  ++conn->cmd_ident);
1845     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1846                                           res_argc, res_argv, res_argv_lens,
1847                                           res_argv_types, conn->cmd_ident);
1848     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1849                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1850                             TRUE);
1851
1852     /* Register pending command callback. After we've received the WHOIS
1853        command reply we will reprocess this command reply by re-calling this
1854        USERS command reply callback. */
1855     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1856                                 get_clients, cmd);
1857
1858     silc_buffer_free(res_cmd);
1859     silc_free(channel_id);
1860     silc_free(res_argv);
1861     silc_free(res_argv_lens);
1862     silc_free(res_argv_types);
1863     return 1;
1864   }
1865
1866   if (wait_res)
1867     return 1;
1868
1869   silc_buffer_push(&client_id_list, (client_id_list.data -
1870                                      client_id_list.head));
1871   silc_buffer_push(&client_mode_list, (client_mode_list.data -
1872                                        client_mode_list.head));
1873
1874   /* Notify application */
1875   if (notify)
1876     COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1877                    &client_mode_list));
1878
1879  out:
1880   silc_free(channel_id);
1881   return 0;
1882 }
1883
1884 /* Reply to USERS command. Received list of client ID's and theirs modes
1885    on the channel we requested. */
1886
1887 SILC_CLIENT_CMD_REPLY_FUNC(users)
1888 {
1889   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1890   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1891   SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1892
1893   SILC_LOG_DEBUG(("Start"));
1894
1895   if (cmd->error != SILC_STATUS_OK) {
1896     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1897         "Query failed: %s", silc_get_status_message(cmd->error));
1898     COMMAND_REPLY_ERROR(cmd->error);
1899     goto out;
1900   }
1901
1902   if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1903     if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1904       /* Do not resolve anymore. Server may be sending us some non-existent
1905          Client ID (a bug in server), and we want to show the users list
1906          anyway. */
1907       silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1908                                            silc_client_command_reply_users_cb,
1909                                            silc_client_command_reply_users);
1910       goto out;
1911     } else {
1912       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1913           "Query failed: %s", silc_get_status_message(cmd->error));
1914       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1915       goto out;
1916     }
1917   }
1918
1919   if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1920                                            silc_client_command_reply_users_cb,
1921                                            silc_client_command_reply_users))
1922     return;
1923
1924  out:
1925   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1926   silc_client_command_reply_free(cmd);
1927 }
1928
1929 /* Received command reply to GETKEY command. WE've received the remote
1930    client's public key. */
1931
1932 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1933 {
1934   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1935   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1936   SilcIDPayload idp = NULL;
1937   SilcClientID *client_id = NULL;
1938   SilcClientEntry client_entry;
1939   SilcServerID *server_id = NULL;
1940   SilcServerEntry server_entry;
1941   unsigned char *tmp;
1942   SilcUInt32 len;
1943   SilcIdType id_type;
1944   SilcPublicKey public_key = NULL;
1945
1946   SILC_LOG_DEBUG(("Start"));
1947
1948   if (cmd->error != SILC_STATUS_OK) {
1949     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1950         "%s", silc_get_status_message(cmd->error));
1951     COMMAND_REPLY_ERROR(cmd->error);
1952     goto out;
1953   }
1954
1955   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1956   if (!tmp) {
1957     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1958     goto out;
1959   }
1960   idp = silc_id_payload_parse(tmp, len);
1961   if (!idp) {
1962     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1963     goto out;
1964   }
1965
1966   /* Get the public key payload */
1967   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1968   if (tmp) {
1969     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1970       public_key = NULL;
1971   }
1972
1973   if (!public_key) {
1974     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1975     goto out;
1976   }
1977
1978   id_type = silc_id_payload_get_type(idp);
1979   if (id_type == SILC_ID_CLIENT) {
1980     /* Received client's public key */
1981     client_id = silc_id_payload_get_id(idp);
1982     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1983     if (!client_entry) {
1984       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1985       goto out;
1986     }
1987
1988     /* Save fingerprint */
1989     if (!client_entry->fingerprint) {
1990       client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1991       client_entry->fingerprint_len = 20;
1992       silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1993                      client_entry->fingerprint);
1994     }
1995     if (!client_entry->public_key) {
1996       client_entry->public_key = public_key;
1997       public_key = NULL;
1998     }
1999
2000     /* Notify application */
2001     COMMAND_REPLY((SILC_ARGS, id_type, client_entry,
2002                    client_entry->public_key));
2003   } else if (id_type == SILC_ID_SERVER) {
2004     /* Received server's public key */
2005     server_id = silc_id_payload_get_id(idp);
2006     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
2007     if (!server_entry) {
2008       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2009       goto out;
2010     }
2011
2012     /* Notify application */
2013     COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
2014   }
2015
2016  out:
2017   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
2018   if (idp)
2019     silc_id_payload_free(idp);
2020   if (public_key)
2021     silc_pkcs_public_key_free(public_key);
2022   silc_free(client_id);
2023   silc_free(server_id);
2024   silc_client_command_reply_free(cmd);
2025 }
2026
2027 SILC_CLIENT_CMD_REPLY_FUNC(quit)
2028 {
2029   silc_client_command_reply_free(context);
2030 }
2031
2032
2033 /******************************************************************************
2034
2035                       Internal command reply functions
2036
2037 ******************************************************************************/
2038
2039 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
2040 {
2041   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2042   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2043
2044   COMMAND_CHECK_STATUS_I;
2045
2046   /* Save WHOIS info */
2047   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
2048
2049   /* Pending callbacks are not executed if this was an list entry */
2050   if (cmd->status != SILC_STATUS_OK &&
2051       cmd->status != SILC_STATUS_LIST_END) {
2052     silc_client_command_reply_free(cmd);
2053     return;
2054   }
2055
2056  out:
2057   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
2058
2059  err:
2060   /* If we received notify for invalid ID we'll remove the ID if we
2061      have it cached. */
2062   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2063     SilcClientEntry client_entry;
2064     SilcUInt32 tmp_len;
2065     unsigned char *tmp =
2066       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2067                                  2, &tmp_len);
2068     if (tmp) {
2069       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
2070       if (client_id) {
2071         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2072                                                     client_id);
2073         if (client_entry)
2074           silc_client_del_client(cmd->client, conn, client_entry);
2075         silc_free(client_id);
2076       }
2077     }
2078   }
2079
2080   /* Unregister this command reply */
2081   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
2082                                  NULL, silc_client_command_reply_whois_i,
2083                                  cmd->ident);
2084
2085   silc_client_command_reply_free(cmd);
2086 }
2087
2088 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
2089 {
2090   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2091   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2092
2093   COMMAND_CHECK_STATUS_I;
2094
2095   /* Save IDENTIFY info */
2096   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
2097
2098   /* Pending callbacks are not executed if this was an list entry */
2099   if (cmd->status != SILC_STATUS_OK &&
2100       cmd->status != SILC_STATUS_LIST_END) {
2101     silc_client_command_reply_free(cmd);
2102     return;
2103   }
2104
2105  out:
2106   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
2107
2108  err:
2109   /* If we received notify for invalid ID we'll remove the ID if we
2110      have it cached. */
2111   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2112     SilcClientEntry client_entry;
2113     SilcUInt32 tmp_len;
2114     unsigned char *tmp =
2115       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2116                                  2, &tmp_len);
2117     if (tmp) {
2118       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
2119       if (client_id) {
2120         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2121                                                     client_id);
2122         if (client_entry)
2123           silc_client_del_client(cmd->client, conn, client_entry);
2124         silc_free(client_id);
2125       }
2126     }
2127   }
2128
2129   /* Unregister this command reply */
2130   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
2131                                  NULL, silc_client_command_reply_identify_i,
2132                                  cmd->ident);
2133
2134   silc_client_command_reply_free(cmd);
2135 }
2136
2137 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2138 {
2139   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2140   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2141   unsigned char *tmp;
2142   SilcServerEntry server;
2143   SilcServerID *server_id = NULL;
2144   char *server_name, *server_info;
2145   SilcUInt32 len;
2146
2147   COMMAND_CHECK_STATUS_I;
2148
2149   /* Get server ID */
2150   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2151   if (!tmp)
2152     goto out;
2153
2154   server_id = silc_id_payload_parse_id(tmp, len, NULL);
2155   if (!server_id)
2156     goto out;
2157
2158   /* Get server name */
2159   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2160   if (!server_name)
2161     goto out;
2162
2163   /* Get server info */
2164   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2165   if (!server_info)
2166     goto out;
2167
2168   /* See whether we have this server cached. If not create it. */
2169   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2170   if (!server) {
2171     SILC_LOG_DEBUG(("New server entry"));
2172     silc_client_add_server(cmd->client, conn, server_name, server_info,
2173                            silc_id_dup(server_id, SILC_ID_SERVER));
2174   }
2175
2176  out:
2177   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2178   silc_free(server_id);
2179  err:
2180   silc_client_command_reply_free(cmd);
2181 }
2182
2183 static void silc_client_command_reply_users_i_cb(SilcClient client,
2184                                                  SilcClientConnection conn,
2185                                                  SilcChannelEntry *channels,
2186                                                  SilcUInt32 channels_count,
2187                                                  void *context)
2188 {
2189   if (!channels_count) {
2190     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2191     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2192
2193     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2194     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2195         "%s", silc_get_status_message(cmd->error));
2196     COMMAND_REPLY_ERROR(cmd->error);
2197     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2198     silc_client_command_reply_free(cmd);
2199     return;
2200   }
2201
2202   silc_client_command_reply_users_i(context, NULL);
2203 }
2204
2205 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2206 {
2207   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2208
2209   COMMAND_CHECK_STATUS_I;
2210
2211   /* Save USERS info */
2212   if (silc_client_command_reply_users_save(
2213                                     cmd, cmd->status, FALSE, TRUE,
2214                                     silc_client_command_reply_users_i_cb,
2215                                     silc_client_command_reply_users_i))
2216     return;
2217
2218  out:
2219   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2220
2221  err:
2222   /* Unregister this command reply */
2223   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2224                                  NULL, silc_client_command_reply_users_i,
2225                                  cmd->ident);
2226
2227   silc_client_command_reply_free(cmd);
2228 }
2229
2230 /* Private range commands, specific to this implementation (and compatible
2231    with SILC Server >= 0.9). */
2232
2233 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2234 {
2235   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2236   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2237
2238   if (cmd->error != SILC_STATUS_OK) {
2239     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2240         "%s", silc_get_status_message(cmd->error));
2241     COMMAND_REPLY_ERROR(cmd->error);
2242     goto out;
2243   }
2244
2245   /* Notify application */
2246   COMMAND_REPLY((SILC_ARGS));
2247
2248  out:
2249   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2250   silc_client_command_reply_free(cmd);
2251 }
2252
2253 SILC_CLIENT_CMD_REPLY_FUNC(close)
2254 {
2255   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2256   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2257
2258   if (cmd->error != SILC_STATUS_OK) {
2259     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2260         "%s", silc_get_status_message(cmd->error));
2261     COMMAND_REPLY_ERROR(cmd->error);
2262     goto out;
2263   }
2264
2265   /* Notify application */
2266   COMMAND_REPLY((SILC_ARGS));
2267
2268  out:
2269   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2270   silc_client_command_reply_free(cmd);
2271 }
2272
2273 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2274 {
2275   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2276   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2277
2278   if (cmd->error != SILC_STATUS_OK) {
2279     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2280         "%s", silc_get_status_message(cmd->error));
2281     COMMAND_REPLY_ERROR(cmd->error);
2282     goto out;
2283   }
2284
2285   /* Notify application */
2286   COMMAND_REPLY((SILC_ARGS));
2287
2288  out:
2289   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2290   silc_client_command_reply_free(cmd);
2291 }