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