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