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