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