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