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