Added support for channel public keys in Irssi SILC client.
[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   SilcPublicKey public_key = NULL;
1221   SilcBufferStruct channel_pubkeys;
1222
1223   if (cmd->error != SILC_STATUS_OK) {
1224     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1225         "%s", silc_get_status_message(cmd->error));
1226     COMMAND_REPLY_ERROR;
1227     goto out;
1228   }
1229
1230   /* Take Channel ID */
1231   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1232   if (!tmp)
1233     goto out;
1234   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1235   if (!channel_id)
1236     goto out;
1237
1238   /* Get the channel entry */
1239   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1240   if (!channel) {
1241     silc_free(channel_id);
1242     COMMAND_REPLY_ERROR;
1243     goto out;
1244   }
1245
1246   /* Get channel mode */
1247   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1248   if (!tmp) {
1249     silc_free(channel_id);
1250     COMMAND_REPLY_ERROR;
1251     goto out;
1252   }
1253
1254   /* Save the mode */
1255   SILC_GET32_MSB(mode, tmp);
1256   channel->mode = mode;
1257
1258   /* Get founder public key */
1259   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1260   if (tmp) {
1261     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1262       public_key = NULL;
1263   }
1264
1265   /* Get channel public key(s) */
1266   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1267   if (tmp)
1268     silc_buffer_set(&channel_pubkeys, tmp, len);
1269
1270   /* Notify application */
1271   COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1272                  tmp ? &channel_pubkeys : NULL));
1273
1274   silc_free(channel_id);
1275
1276  out:
1277   if (public_key)
1278     silc_pkcs_public_key_free(public_key);
1279   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1280   silc_client_command_reply_free(cmd);
1281 }
1282
1283 /* Received reply for CUMODE command */
1284
1285 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1286 {
1287   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1288   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1289   SilcClientID *client_id;
1290   SilcChannelID *channel_id;
1291   SilcClientEntry client_entry;
1292   SilcChannelEntry channel;
1293   SilcChannelUser chu;
1294   unsigned char *modev, *tmp, *id;
1295   SilcUInt32 len, mode;
1296
1297   if (cmd->error != SILC_STATUS_OK) {
1298     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1299         "%s", silc_get_status_message(cmd->error));
1300     COMMAND_REPLY_ERROR;
1301     goto out;
1302   }
1303
1304   /* Get channel mode */
1305   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1306   if (!modev) {
1307     COMMAND_REPLY_ERROR;
1308     goto out;
1309   }
1310
1311   /* Take Channel ID */
1312   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1313   if (!tmp)
1314     goto out;
1315   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1316   if (!channel_id)
1317     goto out;
1318
1319   /* Get the channel entry */
1320   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1321   if (!channel) {
1322     silc_free(channel_id);
1323     COMMAND_REPLY_ERROR;
1324     goto out;
1325   }
1326
1327   /* Get Client ID */
1328   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1329   if (!id) {
1330     silc_free(channel_id);
1331     COMMAND_REPLY_ERROR;
1332     goto out;
1333   }
1334   client_id = silc_id_payload_parse_id(id, len, NULL);
1335   if (!client_id) {
1336     silc_free(channel_id);
1337     COMMAND_REPLY_ERROR;
1338     goto out;
1339   }
1340
1341   /* Get client entry */
1342   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1343   if (!client_entry) {
1344     silc_free(channel_id);
1345     silc_free(client_id);
1346     COMMAND_REPLY_ERROR;
1347     goto out;
1348   }
1349
1350   /* Save the mode */
1351   SILC_GET32_MSB(mode, modev);
1352   chu = silc_client_on_channel(channel, client_entry);
1353   if (chu)
1354     chu->mode = mode;
1355
1356   /* Notify application */
1357   COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1358   silc_free(client_id);
1359   silc_free(channel_id);
1360
1361  out:
1362   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1363   silc_client_command_reply_free(cmd);
1364 }
1365
1366 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1367 {
1368   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1369   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1370
1371   if (cmd->error != SILC_STATUS_OK) {
1372     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1373         "%s", silc_get_status_message(cmd->error));
1374     COMMAND_REPLY_ERROR;
1375     goto out;
1376   }
1377
1378   /* Notify application */
1379   COMMAND_REPLY((SILC_ARGS));
1380
1381  out:
1382   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1383   silc_client_command_reply_free(cmd);
1384 }
1385
1386 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1387 {
1388   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1389   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1390
1391   if (cmd->error != SILC_STATUS_OK) {
1392     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1393         "%s", silc_get_status_message(cmd->error));
1394     COMMAND_REPLY_ERROR;
1395     goto out;
1396   }
1397
1398   /* Notify application */
1399   COMMAND_REPLY((SILC_ARGS));
1400
1401  out:
1402   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1403   silc_client_command_reply_free(cmd);
1404 }
1405
1406 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1407 {
1408   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1409   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1410
1411   if (cmd->error != SILC_STATUS_OK) {
1412     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1413         "%s", silc_get_status_message(cmd->error));
1414     COMMAND_REPLY_ERROR;
1415     goto out;
1416   }
1417
1418   /* Notify application */
1419   COMMAND_REPLY((SILC_ARGS));
1420
1421  out:
1422   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1423   silc_client_command_reply_free(cmd);
1424 }
1425
1426 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1427 {
1428   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1429   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1430   SilcBuffer detach;
1431
1432   if (cmd->error != SILC_STATUS_OK) {
1433     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1434         "%s", silc_get_status_message(cmd->error));
1435     COMMAND_REPLY_ERROR;
1436     goto out;
1437   }
1438
1439   /* Notify application */
1440   COMMAND_REPLY((SILC_ARGS));
1441
1442   /* Generate the detachment data and deliver it to the client in the
1443      detach client operation */
1444   detach = silc_client_get_detach_data(cmd->client, conn);
1445   if (detach) {
1446     cmd->client->internal->ops->detach(cmd->client, conn,
1447                                        detach->data, detach->len);
1448     silc_buffer_free(detach);
1449   }
1450
1451  out:
1452   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1453   silc_client_command_reply_free(cmd);
1454 }
1455
1456 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1457 {
1458   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1459   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1460
1461   if (cmd->error != SILC_STATUS_OK) {
1462     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1463         "%s", silc_get_status_message(cmd->error));
1464     COMMAND_REPLY_ERROR;
1465     goto out;
1466   }
1467
1468   /* Notify application */
1469   COMMAND_REPLY((SILC_ARGS));
1470
1471  out:
1472   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1473   silc_client_command_reply_free(cmd);
1474 }
1475
1476 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1477 {
1478   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1479   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1480   SilcChannelEntry channel;
1481   SilcChannelID *channel_id;
1482   unsigned char *tmp;
1483   SilcUInt32 len;
1484   SilcBufferStruct buf;
1485
1486   if (cmd->error != SILC_STATUS_OK) {
1487     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1488         "%s", silc_get_status_message(cmd->error));
1489     COMMAND_REPLY_ERROR;
1490     goto out;
1491   }
1492
1493   /* Take Channel ID */
1494   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1495   if (!tmp)
1496     goto out;
1497
1498   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1499   if (!channel_id)
1500     goto out;
1501
1502   /* Get the channel entry */
1503   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1504   if (!channel) {
1505     silc_free(channel_id);
1506     COMMAND_REPLY_ERROR;
1507     goto out;
1508   }
1509
1510   /* Get the ban list */
1511   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1512   if (tmp)
1513     silc_buffer_set(&buf, tmp, len);
1514
1515   /* Notify application */
1516   COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1517
1518  out:
1519   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1520   silc_client_command_reply_free(cmd);
1521 }
1522
1523 /* Reply to LEAVE command. */
1524
1525 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1526 {
1527   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1528   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1529   SilcChannelID *channel_id;
1530   SilcChannelEntry channel = NULL;
1531   unsigned char *tmp;
1532   SilcUInt32 len;
1533
1534   if (cmd->error != SILC_STATUS_OK) {
1535     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1536         "%s", silc_get_status_message(cmd->error));
1537     COMMAND_REPLY_ERROR;
1538     goto out;
1539   }
1540
1541   /* From protocol version 1.1 we get the channel ID of the left channel */
1542   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1543   if (tmp) {
1544     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1545     if (!channel_id)
1546       goto out;
1547
1548     /* Get the channel entry */
1549     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1550     if (!channel) {
1551       silc_free(channel_id);
1552       COMMAND_REPLY_ERROR;
1553       goto out;
1554     }
1555
1556     silc_free(channel_id);
1557   }
1558
1559   /* Notify application */
1560   COMMAND_REPLY((SILC_ARGS, channel));
1561
1562  out:
1563   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1564   silc_client_command_reply_free(cmd);
1565 }
1566
1567 /* Channel resolving callback for USERS command reply. */
1568
1569 static void silc_client_command_reply_users_cb(SilcClient client,
1570                                                SilcClientConnection conn,
1571                                                SilcChannelEntry *channels,
1572                                                SilcUInt32 channels_count,
1573                                                void *context)
1574 {
1575   if (!channels_count) {
1576     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1577     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1578
1579     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1580     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1581         "%s", silc_get_status_message(cmd->error));
1582     COMMAND_REPLY_ERROR;
1583     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1584     silc_client_command_reply_free(cmd);
1585     return;
1586   }
1587
1588   silc_client_command_reply_users(context, NULL);
1589 }
1590
1591 static int
1592 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1593                                      SilcStatus status,
1594                                      bool notify,
1595                                      bool resolve,
1596                                      SilcGetChannelCallback get_channel,
1597                                      SilcCommandCb get_clients)
1598 {
1599   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1600   SilcChannelEntry channel;
1601   SilcClientEntry client_entry;
1602   SilcChannelUser chu;
1603   SilcChannelID *channel_id = NULL;
1604   SilcBufferStruct client_id_list, client_mode_list;
1605   unsigned char *tmp;
1606   SilcUInt32 tmp_len, list_count;
1607   int i;
1608   unsigned char **res_argv = NULL;
1609   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1610   bool wait_res = FALSE;
1611
1612   SILC_LOG_DEBUG(("Start"));
1613
1614   /* Get channel ID */
1615   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1616   if (!tmp) {
1617     COMMAND_REPLY_ERROR;
1618     goto out;
1619   }
1620   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1621   if (!channel_id) {
1622     COMMAND_REPLY_ERROR;
1623     goto out;
1624   }
1625
1626   /* Get the list count */
1627   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1628   if (!tmp) {
1629     COMMAND_REPLY_ERROR;
1630     goto out;
1631   }
1632   SILC_GET32_MSB(list_count, tmp);
1633
1634   /* Get Client ID list */
1635   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1636   if (!tmp) {
1637     COMMAND_REPLY_ERROR;
1638     goto out;
1639   }
1640   silc_buffer_set(&client_id_list, tmp, tmp_len);
1641
1642   /* Get client mode list */
1643   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1644   if (!tmp) {
1645     COMMAND_REPLY_ERROR;
1646     goto out;
1647   }
1648   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1649
1650   /* Get channel entry */
1651   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1652   if (!channel) {
1653     /* Resolve the channel from server */
1654     silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1655                                           get_channel, cmd);
1656     silc_free(channel_id);
1657     return 1;
1658   }
1659
1660   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1661
1662   /* Cache the received Client ID's and modes. */
1663   for (i = 0; i < list_count; i++) {
1664     SilcUInt16 idp_len;
1665     SilcUInt32 mode;
1666     SilcClientID *client_id;
1667
1668     /* Client ID */
1669     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1670     idp_len += 4;
1671     client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1672     if (!client_id)
1673       continue;
1674
1675     /* Mode */
1676     SILC_GET32_MSB(mode, client_mode_list.data);
1677
1678     /* Check if we have this client cached already. */
1679     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1680     if (!client_entry || !client_entry->username || !client_entry->realname) {
1681       if (resolve) {
1682         /* No we don't have it (or it is incomplete in information), query
1683            it from the server. Assemble argument table that will be sent
1684            for the WHOIS command later. */
1685         res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1686                                 (res_argc + 1));
1687         res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1688                                      (res_argc + 1));
1689         res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1690                                       (res_argc + 1));
1691         res_argv[res_argc] = client_id_list.data;
1692         res_argv_lens[res_argc] = idp_len;
1693         res_argv_types[res_argc] = res_argc + 4;
1694         res_argc++;
1695       }
1696     } else {
1697       if (!silc_client_on_channel(channel, client_entry)) {
1698         chu = silc_calloc(1, sizeof(*chu));
1699         chu->client = client_entry;
1700         chu->mode = mode;
1701         chu->channel = channel;
1702         silc_hash_table_add(channel->user_list, client_entry, chu);
1703         silc_hash_table_add(client_entry->channels, channel, chu);
1704       }
1705     }
1706
1707     silc_free(client_id);
1708     silc_buffer_pull(&client_id_list, idp_len);
1709     silc_buffer_pull(&client_mode_list, 4);
1710   }
1711
1712   /* Query the client information from server if the list included clients
1713      that we don't know about. */
1714   if (res_argc) {
1715     SilcBuffer res_cmd;
1716
1717     /* Send the WHOIS command to server */
1718     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1719                                  silc_client_command_reply_whois_i, 0,
1720                                  ++conn->cmd_ident);
1721     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1722                                           res_argc, res_argv, res_argv_lens,
1723                                           res_argv_types, conn->cmd_ident);
1724     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1725                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1726                             TRUE);
1727
1728     /* Register pending command callback. After we've received the WHOIS
1729        command reply we will reprocess this command reply by re-calling this
1730        USERS command reply callback. */
1731     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1732                                 get_clients, cmd);
1733
1734     silc_buffer_free(res_cmd);
1735     silc_free(channel_id);
1736     silc_free(res_argv);
1737     silc_free(res_argv_lens);
1738     silc_free(res_argv_types);
1739     return 1;
1740   }
1741
1742   if (wait_res)
1743     return 1;
1744
1745   silc_buffer_push(&client_id_list, (client_id_list.data -
1746                                      client_id_list.head));
1747   silc_buffer_push(&client_mode_list, (client_mode_list.data -
1748                                        client_mode_list.head));
1749
1750   /* Notify application */
1751   if (notify)
1752     COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1753                    &client_mode_list));
1754
1755  out:
1756   silc_free(channel_id);
1757   return 0;
1758 }
1759
1760 /* Reply to USERS command. Received list of client ID's and theirs modes
1761    on the channel we requested. */
1762
1763 SILC_CLIENT_CMD_REPLY_FUNC(users)
1764 {
1765   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1766   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1767   SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1768
1769   SILC_LOG_DEBUG(("Start"));
1770
1771   if (cmd->error != SILC_STATUS_OK) {
1772     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1773         "%s", silc_get_status_message(cmd->error));
1774     COMMAND_REPLY_ERROR;
1775     goto out;
1776   }
1777
1778   if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1779     if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1780       /* Do not resolve anymore. Server may be sending us some non-existent
1781          Client ID (a bug in server), and we want to show the users list
1782          anyway. */
1783       silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1784                                            silc_client_command_reply_users_cb,
1785                                            silc_client_command_reply_users);
1786       goto out;
1787     } else {
1788       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1789           "%s", silc_get_status_message(cmd->error));
1790       COMMAND_REPLY_ERROR;
1791       goto out;
1792     }
1793   }
1794
1795   if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1796                                            silc_client_command_reply_users_cb,
1797                                            silc_client_command_reply_users))
1798     return;
1799
1800  out:
1801   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1802   silc_client_command_reply_free(cmd);
1803 }
1804
1805 /* Received command reply to GETKEY command. WE've received the remote
1806    client's public key. */
1807
1808 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1809 {
1810   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1811   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1812   SilcIDPayload idp = NULL;
1813   SilcClientID *client_id = NULL;
1814   SilcClientEntry client_entry;
1815   SilcServerID *server_id = NULL;
1816   SilcServerEntry server_entry;
1817   unsigned char *tmp;
1818   SilcUInt32 len;
1819   SilcIdType id_type;
1820   SilcPublicKey public_key = NULL;
1821
1822   SILC_LOG_DEBUG(("Start"));
1823
1824   if (cmd->error != SILC_STATUS_OK) {
1825     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1826         "%s", silc_get_status_message(cmd->error));
1827     COMMAND_REPLY_ERROR;
1828     goto out;
1829   }
1830
1831   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1832   if (!tmp) {
1833     COMMAND_REPLY_ERROR;
1834     goto out;
1835   }
1836   idp = silc_id_payload_parse(tmp, len);
1837   if (!idp) {
1838     COMMAND_REPLY_ERROR;
1839     goto out;
1840   }
1841
1842   /* Get the public key payload */
1843   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1844   if (tmp) {
1845     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1846       public_key = NULL;
1847   }
1848
1849   id_type = silc_id_payload_get_type(idp);
1850   if (id_type == SILC_ID_CLIENT) {
1851     /* Received client's public key */
1852     client_id = silc_id_payload_get_id(idp);
1853     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1854     if (!client_entry) {
1855       COMMAND_REPLY_ERROR;
1856       goto out;
1857     }
1858
1859     /* Save fingerprint */
1860     if (!client_entry->fingerprint) {
1861       client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1862       client_entry->fingerprint_len = 20;
1863       silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1864                      client_entry->fingerprint);
1865     }
1866
1867     /* Notify application */
1868     COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1869   } else if (id_type == SILC_ID_SERVER) {
1870     /* Received server's public key */
1871     server_id = silc_id_payload_get_id(idp);
1872     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1873     if (!server_entry) {
1874       COMMAND_REPLY_ERROR;
1875       goto out;
1876     }
1877
1878     /* Notify application */
1879     COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1880   }
1881
1882  out:
1883   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1884   if (idp)
1885     silc_id_payload_free(idp);
1886   if (public_key)
1887     silc_pkcs_public_key_free(public_key);
1888   silc_free(client_id);
1889   silc_free(server_id);
1890   silc_client_command_reply_free(cmd);
1891 }
1892
1893 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1894 {
1895   silc_client_command_reply_free(context);
1896 }
1897
1898
1899 /******************************************************************************
1900
1901                       Internal command reply functions
1902
1903 ******************************************************************************/
1904
1905 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1906 {
1907   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1908   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1909
1910   COMMAND_CHECK_STATUS_I;
1911
1912   /* Save WHOIS info */
1913   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1914
1915   /* Pending callbacks are not executed if this was an list entry */
1916   if (cmd->status != SILC_STATUS_OK &&
1917       cmd->status != SILC_STATUS_LIST_END) {
1918     silc_client_command_reply_free(cmd);
1919     return;
1920   }
1921
1922  out:
1923   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1924
1925  err:
1926   /* If we received notify for invalid ID we'll remove the ID if we
1927      have it cached. */
1928   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1929     SilcClientEntry client_entry;
1930     SilcUInt32 tmp_len;
1931     unsigned char *tmp =
1932       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1933                                  2, &tmp_len);
1934     if (tmp) {
1935       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1936       if (client_id) {
1937         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1938                                                     client_id);
1939         if (client_entry)
1940           silc_client_del_client(cmd->client, conn, client_entry);
1941         silc_free(client_id);
1942       }
1943     }
1944   }
1945
1946   /* Unregister this command reply */
1947   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1948                                  NULL, silc_client_command_reply_whois_i,
1949                                  cmd->ident);
1950
1951   silc_client_command_reply_free(cmd);
1952 }
1953
1954 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1955 {
1956   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1957   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1958
1959   COMMAND_CHECK_STATUS_I;
1960
1961   /* Save IDENTIFY info */
1962   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1963
1964   /* Pending callbacks are not executed if this was an list entry */
1965   if (cmd->status != SILC_STATUS_OK &&
1966       cmd->status != SILC_STATUS_LIST_END) {
1967     silc_client_command_reply_free(cmd);
1968     return;
1969   }
1970
1971  out:
1972   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1973
1974  err:
1975   /* If we received notify for invalid ID we'll remove the ID if we
1976      have it cached. */
1977   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1978     SilcClientEntry client_entry;
1979     SilcUInt32 tmp_len;
1980     unsigned char *tmp =
1981       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1982                                  2, &tmp_len);
1983     if (tmp) {
1984       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1985       if (client_id) {
1986         client_entry = silc_client_get_client_by_id(cmd->client, conn,
1987                                                     client_id);
1988         if (client_entry)
1989           silc_client_del_client(cmd->client, conn, client_entry);
1990         silc_free(client_id);
1991       }
1992     }
1993   }
1994
1995   /* Unregister this command reply */
1996   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1997                                  NULL, silc_client_command_reply_identify_i,
1998                                  cmd->ident);
1999
2000   silc_client_command_reply_free(cmd);
2001 }
2002
2003 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2004 {
2005   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2006   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2007   unsigned char *tmp;
2008   SilcServerEntry server;
2009   SilcServerID *server_id = NULL;
2010   char *server_name, *server_info;
2011   SilcUInt32 len;
2012
2013   COMMAND_CHECK_STATUS_I;
2014
2015   /* Get server ID */
2016   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2017   if (!tmp)
2018     goto out;
2019
2020   server_id = silc_id_payload_parse_id(tmp, len, NULL);
2021   if (!server_id)
2022     goto out;
2023
2024   /* Get server name */
2025   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2026   if (!server_name)
2027     goto out;
2028
2029   /* Get server info */
2030   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2031   if (!server_info)
2032     goto out;
2033
2034   /* See whether we have this server cached. If not create it. */
2035   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2036   if (!server) {
2037     SILC_LOG_DEBUG(("New server entry"));
2038     silc_client_add_server(cmd->client, conn, server_name, server_info,
2039                            silc_id_dup(server_id, SILC_ID_SERVER));
2040   }
2041
2042  out:
2043   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2044   silc_free(server_id);
2045  err:
2046   silc_client_command_reply_free(cmd);
2047 }
2048
2049 static void silc_client_command_reply_users_i_cb(SilcClient client,
2050                                                  SilcClientConnection conn,
2051                                                  SilcChannelEntry *channels,
2052                                                  SilcUInt32 channels_count,
2053                                                  void *context)
2054 {
2055   if (!channels_count) {
2056     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2057     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2058
2059     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2060     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2061         "%s", silc_get_status_message(cmd->error));
2062     COMMAND_REPLY_ERROR;
2063     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2064     silc_client_command_reply_free(cmd);
2065     return;
2066   }
2067
2068   silc_client_command_reply_users_i(context, NULL);
2069 }
2070
2071 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2072 {
2073   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2074
2075   COMMAND_CHECK_STATUS_I;
2076
2077   /* Save USERS info */
2078   if (silc_client_command_reply_users_save(
2079                                     cmd, cmd->status, FALSE, TRUE,
2080                                     silc_client_command_reply_users_i_cb,
2081                                     silc_client_command_reply_users_i))
2082     return;
2083
2084  out:
2085   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2086
2087  err:
2088   /* Unregister this command reply */
2089   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2090                                  NULL, silc_client_command_reply_users_i,
2091                                  cmd->ident);
2092
2093   silc_client_command_reply_free(cmd);
2094 }
2095
2096 /* Private range commands, specific to this implementation (and compatible
2097    with SILC Server >= 0.9). */
2098
2099 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2100 {
2101   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2102   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2103
2104   if (cmd->error != SILC_STATUS_OK) {
2105     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2106         "%s", silc_get_status_message(cmd->error));
2107     COMMAND_REPLY_ERROR;
2108     goto out;
2109   }
2110
2111   /* Notify application */
2112   COMMAND_REPLY((SILC_ARGS));
2113
2114  out:
2115   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2116   silc_client_command_reply_free(cmd);
2117 }
2118
2119 SILC_CLIENT_CMD_REPLY_FUNC(close)
2120 {
2121   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2122   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2123
2124   if (cmd->error != SILC_STATUS_OK) {
2125     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2126         "%s", silc_get_status_message(cmd->error));
2127     COMMAND_REPLY_ERROR;
2128     goto out;
2129   }
2130
2131   /* Notify application */
2132   COMMAND_REPLY((SILC_ARGS));
2133
2134  out:
2135   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2136   silc_client_command_reply_free(cmd);
2137 }
2138
2139 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2140 {
2141   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2142   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2143
2144   if (cmd->error != SILC_STATUS_OK) {
2145     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2146         "%s", silc_get_status_message(cmd->error));
2147     COMMAND_REPLY_ERROR;
2148     goto out;
2149   }
2150
2151   /* Notify application */
2152   COMMAND_REPLY((SILC_ARGS));
2153
2154  out:
2155   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2156   silc_client_command_reply_free(cmd);
2157 }