4e226e810d6bdfeb5e631bb6af61b6a873a7c49e
[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 - 2004 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(cmd->status);                         \
52       goto out;                                                 \
53     }                                                           \
54     /* List of errors */                                        \
55     COMMAND_REPLY_ERROR(cmd->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 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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
389     return;
390   }
391   idp = silc_id_payload_parse(id_data, len);
392   if (!idp) {
393     if (notify)
394       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
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   SilcClientID old_client_id;
543
544   SILC_LOG_DEBUG(("Start"));
545
546   if (cmd->error != SILC_STATUS_OK) {
547     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
548         "Cannot set nickname: %s",
549         silc_get_status_message(cmd->error));
550     COMMAND_REPLY_ERROR(cmd->error);
551     goto out;
552   }
553
554   argc = silc_argument_get_arg_num(cmd->args);
555   if (argc < 2 || argc > 3) {
556     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
557         "Cannot set nickname: bad reply to command");
558     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
559     goto out;
560   }
561
562   /* Save old Client ID */
563   old_client_id = *conn->local_id;
564
565   /* Take received Client ID */
566   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
567   idp = silc_id_payload_parse(tmp, len);
568   if (!idp) {
569     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
570     goto out;
571   }
572   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
573
574   /* Take the new nickname too */
575   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
576   if (tmp) {
577     silc_idcache_del_by_context(conn->internal->client_cache,
578                                 conn->local_entry);
579     if (conn->nickname)
580       silc_free(conn->nickname);
581     conn->nickname = strdup(tmp);
582     conn->local_entry->nickname = conn->nickname;
583     silc_client_nickname_format(cmd->client, conn, conn->local_entry);
584     silc_idcache_add(conn->internal->client_cache, strdup(tmp),
585                      conn->local_entry->id, conn->local_entry, 0, NULL);
586   }
587
588   /* Notify application */
589   COMMAND_REPLY((SILC_ARGS, conn->local_entry, conn->local_entry->nickname,
590                  (const SilcClientID *)&old_client_id));
591
592  out:
593   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
594   silc_client_command_reply_free(cmd);
595 }
596
597 /* Received reply to the LIST command. */
598
599 SILC_CLIENT_CMD_REPLY_FUNC(list)
600 {
601   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
602   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
603   unsigned char *tmp, *name, *topic;
604   SilcUInt32 usercount = 0, len;
605   SilcChannelID *channel_id = NULL;
606   SilcChannelEntry channel_entry;
607
608   COMMAND_CHECK_STATUS;
609
610   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
611   if (!tmp) {
612     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
613     goto out;
614   }
615
616   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
617   if (!channel_id) {
618     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
619     goto out;
620   }
621
622   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
623   if (!name) {
624     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
625     goto out;
626   }
627
628   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
629   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
630   if (tmp)
631     SILC_GET32_MSB(usercount, tmp);
632
633   /* Check whether the channel exists, and add it to cache if it doesn't. */
634   channel_entry = silc_client_get_channel_by_id(cmd->client, conn,
635                                                 channel_id);
636   if (!channel_entry) {
637     /* Add new channel entry */
638     channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
639                                             channel_id);
640     if (!channel_entry) {
641       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
642       goto out;
643     }
644     channel_id = NULL;
645   }
646
647   /* Notify application */
648   COMMAND_REPLY((SILC_ARGS, channel_entry, name, topic, usercount));
649
650   /* Pending callbacks are not executed if this was an list entry */
651   if (cmd->status != SILC_STATUS_OK &&
652       cmd->status != SILC_STATUS_LIST_END) {
653     silc_client_command_reply_free(cmd);
654     return;
655   }
656
657  out:
658   silc_free(channel_id);
659   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
660  err:
661   silc_client_command_reply_free(cmd);
662 }
663
664 /* Received reply to topic command. */
665
666 SILC_CLIENT_CMD_REPLY_FUNC(topic)
667 {
668   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
669   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
670   SilcChannelEntry channel;
671   SilcChannelID *channel_id = NULL;
672   unsigned char *tmp;
673   char *topic;
674   SilcUInt32 argc, len;
675
676   if (cmd->error != SILC_STATUS_OK) {
677     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
678         "Cannot set topic: %s", silc_get_status_message(cmd->error));
679     COMMAND_REPLY_ERROR(cmd->error);
680     goto out;
681   }
682
683   argc = silc_argument_get_arg_num(cmd->args);
684   if (argc < 1 || argc > 3) {
685     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
686     goto out;
687   }
688
689   /* Take Channel ID */
690   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
691   if (!tmp)
692     goto out;
693
694   /* Take topic */
695   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
696   if (!topic)
697     goto out;
698
699   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
700   if (!channel_id)
701     goto out;
702
703   /* Get the channel entry */
704   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
705   if (!channel) {
706     silc_free(channel_id);
707     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
708     goto out;
709   }
710
711   if (topic) {
712     silc_free(channel->topic);
713     channel->topic = silc_memdup(topic, strlen(topic));
714   }
715
716   /* Notify application */
717   COMMAND_REPLY((SILC_ARGS, channel, topic));
718
719  out:
720   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
721   silc_client_command_reply_free(cmd);
722 }
723
724 /* Received reply to invite command. */
725
726 SILC_CLIENT_CMD_REPLY_FUNC(invite)
727 {
728   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
729   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
730   SilcChannelEntry channel;
731   SilcChannelID *channel_id;
732   unsigned char *tmp;
733   SilcUInt32 len;
734   SilcBufferStruct buf;
735
736   if (cmd->error != SILC_STATUS_OK) {
737     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
738         "Cannot invite: %s", silc_get_status_message(cmd->error));
739     COMMAND_REPLY_ERROR(cmd->error);
740     goto out;
741   }
742
743   /* Take Channel ID */
744   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
745   if (!tmp)
746     goto out;
747
748   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
749   if (!channel_id)
750     goto out;
751
752   /* Get the channel entry */
753   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
754   if (!channel) {
755     silc_free(channel_id);
756     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
757     goto out;
758   }
759
760   /* Get the invite list */
761   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
762   if (tmp)
763     silc_buffer_set(&buf, tmp, len);
764
765   /* Notify application */
766   COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
767
768  out:
769   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
770   silc_client_command_reply_free(cmd);
771 }
772
773 /* Received reply to the KILL command. */
774
775 SILC_CLIENT_CMD_REPLY_FUNC(kill)
776 {
777   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
778   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
779   SilcClientID *client_id;
780   SilcClientEntry client_entry = NULL;
781   SilcUInt32 len;
782   unsigned char *id_data;
783
784   if (cmd->error != SILC_STATUS_OK) {
785     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
786         "Cannot kill: %s", silc_get_status_message(cmd->error));
787     COMMAND_REPLY_ERROR(cmd->error);
788     goto out;
789   }
790
791   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
792   if (id_data) {
793     client_id = silc_id_payload_parse_id(id_data, len, NULL);
794     if (!client_id) {
795       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
796       goto out;
797     }
798
799     /* Get the client entry, if exists */
800     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
801     silc_free(client_id);
802   }
803
804   /* Notify application */
805   COMMAND_REPLY((SILC_ARGS, client_entry));
806
807  out:
808   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
809   silc_client_command_reply_free(cmd);
810 }
811
812 /* Received reply to INFO command. We receive the server ID and some
813    information about the server user requested. */
814
815 SILC_CLIENT_CMD_REPLY_FUNC(info)
816 {
817   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
818   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
819   unsigned char *tmp;
820   SilcServerEntry server;
821   SilcServerID *server_id = NULL;
822   char *server_name, *server_info;
823   SilcUInt32 len;
824
825   SILC_LOG_DEBUG(("Start"));
826
827   if (cmd->error != SILC_STATUS_OK) {
828     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
829         silc_get_status_message(cmd->error));
830     COMMAND_REPLY_ERROR(cmd->error);
831     goto out;
832   }
833
834   /* Get server ID */
835   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
836   if (!tmp)
837     goto out;
838
839   server_id = silc_id_payload_parse_id(tmp, len, NULL);
840   if (!server_id)
841     goto out;
842
843   /* Get server name */
844   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
845   if (!server_name)
846     goto out;
847
848   /* Get server info */
849   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
850   if (!server_info)
851     goto out;
852
853   /* See whether we have this server cached. If not create it. */
854   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
855   if (!server) {
856     SILC_LOG_DEBUG(("New server entry"));
857     server = silc_client_add_server(cmd->client, conn, server_name,
858                                     server_info,
859                                     silc_id_dup(server_id, SILC_ID_SERVER));
860     if (!server)
861       goto out;
862   }
863
864   /* Notify application */
865   COMMAND_REPLY((SILC_ARGS, server, server->server_name, server->server_info));
866
867  out:
868   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
869   silc_free(server_id);
870   silc_client_command_reply_free(cmd);
871 }
872
873 /* Received reply to STATS command. */
874
875 SILC_CLIENT_CMD_REPLY_FUNC(stats)
876 {
877   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
878   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
879   unsigned char *tmp, *buf = NULL;
880   SilcUInt32 len, buf_len = 0;
881
882   if (cmd->error != SILC_STATUS_OK) {
883     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
884         "%s", silc_get_status_message(cmd->error));
885     COMMAND_REPLY_ERROR(cmd->error);
886     goto out;
887   }
888
889   /* Get server ID */
890   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
891   if (!tmp)
892     goto out;
893
894   /* Get statistics structure */
895   buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
896
897   /* Notify application */
898   COMMAND_REPLY((SILC_ARGS, buf, buf_len));
899
900  out:
901   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
902   silc_client_command_reply_free(cmd);
903 }
904
905 /* Received reply to PING command. The reply time is shown to user. */
906
907 SILC_CLIENT_CMD_REPLY_FUNC(ping)
908 {
909   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
910   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
911   void *id;
912   int i;
913   time_t diff, curtime;
914
915   if (cmd->error != SILC_STATUS_OK) {
916     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
917         "%s", silc_get_status_message(cmd->error));
918     COMMAND_REPLY_ERROR(cmd->error);
919     goto out;
920   }
921
922   curtime = time(NULL);
923   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
924                       cmd->packet->src_id_type);
925   if (!id || !conn->internal->ping) {
926     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
927     goto out;
928   }
929
930   for (i = 0; i < conn->internal->ping_count; i++) {
931     if (!conn->internal->ping[i].dest_id)
932       continue;
933     if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) {
934       diff = curtime - conn->internal->ping[i].start_time;
935       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
936           "Ping reply from %s: %d second%s",
937           conn->internal->ping[i].dest_name, diff,
938           diff == 1 ? "" : "s");
939
940       conn->internal->ping[i].start_time = 0;
941       silc_free(conn->internal->ping[i].dest_id);
942       conn->internal->ping[i].dest_id = NULL;
943       silc_free(conn->internal->ping[i].dest_name);
944       conn->internal->ping[i].dest_name = NULL;
945       break;
946     }
947   }
948
949   silc_free(id);
950
951   /* Notify application */
952   COMMAND_REPLY((SILC_ARGS));
953
954  out:
955   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
956   silc_client_command_reply_free(cmd);
957 }
958
959 /* Received reply for JOIN command. */
960
961 SILC_CLIENT_CMD_REPLY_FUNC(join)
962 {
963   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
964   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
965   SilcChannelEntry channel;
966   SilcChannelUser chu;
967   SilcChannelID *channel_id;
968   SilcUInt32 argc, mode = 0, len, list_count;
969   char *topic, *tmp, *channel_name = NULL, *hmac;
970   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
971   SilcBufferStruct chpklist;
972   int i;
973
974   SILC_LOG_DEBUG(("Start"));
975
976   if (cmd->error != SILC_STATUS_OK) {
977     if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
978       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
979           "Cannot join channel: %s", silc_get_status_message(cmd->error));
980     COMMAND_REPLY_ERROR(cmd->error);
981     goto out;
982   }
983
984   argc = silc_argument_get_arg_num(cmd->args);
985   if (argc < 7) {
986     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
987         "Cannot join channel: Bad reply packet");
988     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
989     goto out;
990   }
991
992   /* Get channel name */
993   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
994   if (!tmp) {
995     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
996         "Cannot join channel: Bad reply packet");
997     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
998     goto out;
999   }
1000   channel_name = tmp;
1001
1002   /* Get Channel ID */
1003   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1004   if (!tmp) {
1005     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1006         "Cannot join channel: Bad reply packet");
1007     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1008     goto out;
1009   }
1010   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1011   if (!channel_id) {
1012     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1013     goto out;
1014   }
1015
1016   /* Get channel mode */
1017   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1018   if (tmp)
1019     SILC_GET32_MSB(mode, tmp);
1020
1021   /* Get channel key */
1022   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
1023   if (tmp) {
1024     keyp = silc_buffer_alloc(len);
1025     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
1026     silc_buffer_put(keyp, tmp, len);
1027   }
1028
1029   /* Get topic */
1030   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1031
1032   /* Check whether we have this channel entry already. */
1033   channel = silc_client_get_channel(cmd->client, conn, channel_name);
1034   if (channel) {
1035     if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1036       silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1037   } else {
1038     /* Create new channel entry */
1039     channel = silc_client_add_channel(cmd->client, conn, channel_name,
1040                                       mode, channel_id);
1041   }
1042
1043   conn->current_channel = channel;
1044   channel->mode = mode;
1045
1046   /* Get hmac */
1047   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1048   if (hmac) {
1049     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1050       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1051           "Cannot join channel: Unsupported HMAC `%s'", hmac);
1052       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1053       goto out;
1054     }
1055   }
1056
1057   /* Get the list count */
1058   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1059   if (!tmp)
1060     goto out;
1061   SILC_GET32_MSB(list_count, tmp);
1062
1063   /* Get Client ID list */
1064   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1065   if (!tmp)
1066     goto out;
1067
1068   client_id_list = silc_buffer_alloc(len);
1069   silc_buffer_pull_tail(client_id_list, len);
1070   silc_buffer_put(client_id_list, tmp, len);
1071
1072   /* Get client mode list */
1073   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1074   if (!tmp)
1075     goto out;
1076
1077   client_mode_list = silc_buffer_alloc(len);
1078   silc_buffer_pull_tail(client_mode_list, len);
1079   silc_buffer_put(client_mode_list, tmp, len);
1080
1081   /* Add clients we received in the reply to the channel */
1082   for (i = 0; i < list_count; i++) {
1083     SilcUInt16 idp_len;
1084     SilcUInt32 mode;
1085     SilcClientID *client_id;
1086     SilcClientEntry client_entry;
1087
1088     /* Client ID */
1089     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1090     idp_len += 4;
1091     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1092     if (!client_id)
1093       continue;
1094
1095     /* Mode */
1096     SILC_GET32_MSB(mode, client_mode_list->data);
1097
1098     /* Check if we have this client cached already. */
1099     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1100     if (!client_entry) {
1101       /* No, we don't have it, add entry for it. */
1102       client_entry =
1103         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1104                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1105     }
1106
1107     /* Join client to the channel */
1108     if (!silc_client_on_channel(channel, client_entry)) {
1109       chu = silc_calloc(1, sizeof(*chu));
1110       chu->client = client_entry;
1111       chu->channel = channel;
1112       chu->mode = mode;
1113       silc_hash_table_add(channel->user_list, client_entry, chu);
1114       silc_hash_table_add(client_entry->channels, channel, chu);
1115     }
1116
1117     silc_free(client_id);
1118     silc_buffer_pull(client_id_list, idp_len);
1119     silc_buffer_pull(client_mode_list, 4);
1120   }
1121   silc_buffer_push(client_id_list, client_id_list->data -
1122                    client_id_list->head);
1123   silc_buffer_push(client_mode_list, client_mode_list->data -
1124                    client_mode_list->head);
1125
1126   /* Save channel key */
1127   if (keyp)
1128     silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1129
1130   /* Get founder key */
1131   tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
1132   if (tmp) {
1133     if (channel->founder_key)
1134       silc_pkcs_public_key_free(channel->founder_key);
1135     channel->founder_key = NULL;
1136     silc_pkcs_public_key_payload_decode(tmp, len, &channel->founder_key);
1137   }
1138
1139   /* Get channel public key list */
1140   tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
1141   if (tmp)
1142     silc_buffer_set(&chpklist, tmp, len);
1143
1144   if (topic) {
1145     silc_free(channel->topic);
1146     channel->topic = silc_memdup(topic, strlen(topic));
1147   }
1148
1149   /* Notify application */
1150   COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
1151                  keyp ? keyp->head : NULL, NULL,
1152                  NULL, topic, hmac, list_count, client_id_list,
1153                  client_mode_list, channel->founder_key,
1154                  tmp ? &chpklist : NULL));
1155
1156  out:
1157   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1158   silc_client_command_reply_free(cmd);
1159   silc_buffer_free(keyp);
1160   silc_buffer_free(client_id_list);
1161   silc_buffer_free(client_mode_list);
1162 }
1163
1164 /* Received reply for MOTD command */
1165
1166 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1167 {
1168   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1169   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1170   SilcUInt32 argc, i;
1171   char *motd = NULL, *cp, line[256];
1172
1173   if (cmd->error != SILC_STATUS_OK) {
1174     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1175         "%s", silc_get_status_message(cmd->error));
1176     COMMAND_REPLY_ERROR(cmd->error);
1177     return;
1178   }
1179
1180   argc = silc_argument_get_arg_num(cmd->args);
1181   if (argc > 3) {
1182     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1183     goto out;
1184   }
1185
1186   if (argc == 3) {
1187     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1188     if (!motd) {
1189       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1190       goto out;
1191     }
1192
1193     i = 0;
1194     cp = motd;
1195     while(cp[i] != 0) {
1196       if (cp[i++] == '\n') {
1197         memset(line, 0, sizeof(line));
1198         silc_strncat(line, sizeof(line), cp, i - 1);
1199         cp += i;
1200
1201         if (i == 2)
1202           line[0] = ' ';
1203
1204         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1205
1206         if (!strlen(cp))
1207           break;
1208         i = 0;
1209       }
1210     }
1211   }
1212
1213   /* Notify application */
1214   COMMAND_REPLY((SILC_ARGS, motd));
1215
1216  out:
1217   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1218   silc_client_command_reply_free(cmd);
1219 }
1220
1221 /* Received reply tot he UMODE command. Save the current user mode */
1222
1223 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1224 {
1225   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1226   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1227   unsigned char *tmp;
1228   SilcUInt32 mode;
1229
1230   if (cmd->error != SILC_STATUS_OK) {
1231     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1232         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1233     COMMAND_REPLY_ERROR(cmd->error);
1234     goto out;
1235   }
1236
1237   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1238   if (!tmp) {
1239     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1240     goto out;
1241   }
1242
1243   SILC_GET32_MSB(mode, tmp);
1244   conn->local_entry->mode = mode;
1245
1246   /* Notify application */
1247   COMMAND_REPLY((SILC_ARGS, mode));
1248
1249  out:
1250   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1251   silc_client_command_reply_free(cmd);
1252 }
1253
1254 /* Received reply for CMODE command. */
1255
1256 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1257 {
1258   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1259   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1260   unsigned char *tmp;
1261   SilcUInt32 mode;
1262   SilcChannelID *channel_id;
1263   SilcChannelEntry channel;
1264   SilcUInt32 len;
1265   SilcPublicKey public_key = NULL;
1266   SilcBufferStruct channel_pubkeys;
1267
1268   if (cmd->error != SILC_STATUS_OK) {
1269     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1270         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1271     COMMAND_REPLY_ERROR(cmd->error);
1272     goto out;
1273   }
1274
1275   /* Take Channel ID */
1276   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1277   if (!tmp)
1278     goto out;
1279   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1280   if (!channel_id)
1281     goto out;
1282
1283   /* Get the channel entry */
1284   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1285   if (!channel) {
1286     silc_free(channel_id);
1287     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1288     goto out;
1289   }
1290
1291   /* Get channel mode */
1292   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1293   if (!tmp) {
1294     silc_free(channel_id);
1295     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1296     goto out;
1297   }
1298
1299   /* Save the mode */
1300   SILC_GET32_MSB(mode, tmp);
1301   channel->mode = mode;
1302
1303   /* Get founder public key */
1304   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1305   if (tmp) {
1306     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1307       public_key = NULL;
1308   }
1309
1310   /* Get channel public key(s) */
1311   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1312   if (tmp)
1313     silc_buffer_set(&channel_pubkeys, tmp, len);
1314
1315   /* Notify application */
1316   COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1317                  tmp ? &channel_pubkeys : NULL));
1318
1319   silc_free(channel_id);
1320
1321  out:
1322   if (public_key)
1323     silc_pkcs_public_key_free(public_key);
1324   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1325   silc_client_command_reply_free(cmd);
1326 }
1327
1328 /* Received reply for CUMODE command */
1329
1330 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1331 {
1332   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1333   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1334   SilcClientID *client_id;
1335   SilcChannelID *channel_id;
1336   SilcClientEntry client_entry;
1337   SilcChannelEntry channel;
1338   SilcChannelUser chu;
1339   unsigned char *modev, *tmp, *id;
1340   SilcUInt32 len, mode;
1341
1342   if (cmd->error != SILC_STATUS_OK) {
1343     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1344         "Cannot change mode: %s", silc_get_status_message(cmd->error));
1345     COMMAND_REPLY_ERROR(cmd->error);
1346     goto out;
1347   }
1348
1349   /* Get channel mode */
1350   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1351   if (!modev) {
1352     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1353     goto out;
1354   }
1355
1356   /* Take Channel ID */
1357   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1358   if (!tmp)
1359     goto out;
1360   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1361   if (!channel_id)
1362     goto out;
1363
1364   /* Get the channel entry */
1365   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1366   if (!channel) {
1367     silc_free(channel_id);
1368     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1369     goto out;
1370   }
1371
1372   /* Get Client ID */
1373   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1374   if (!id) {
1375     silc_free(channel_id);
1376     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1377     goto out;
1378   }
1379   client_id = silc_id_payload_parse_id(id, len, NULL);
1380   if (!client_id) {
1381     silc_free(channel_id);
1382     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1383     goto out;
1384   }
1385
1386   /* Get client entry */
1387   client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1388   if (!client_entry) {
1389     silc_free(channel_id);
1390     silc_free(client_id);
1391     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1392     goto out;
1393   }
1394
1395   /* Save the mode */
1396   SILC_GET32_MSB(mode, modev);
1397   chu = silc_client_on_channel(channel, client_entry);
1398   if (chu)
1399     chu->mode = mode;
1400
1401   /* Notify application */
1402   COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1403   silc_free(client_id);
1404   silc_free(channel_id);
1405
1406  out:
1407   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1408   silc_client_command_reply_free(cmd);
1409 }
1410
1411 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1412 {
1413   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1414   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1415   SilcClientID *client_id = NULL;
1416   SilcChannelID *channel_id = NULL;
1417   SilcClientEntry client_entry = NULL;
1418   SilcChannelEntry channel = NULL;
1419   unsigned char *tmp;
1420   SilcUInt32 len;
1421
1422   if (cmd->error != SILC_STATUS_OK) {
1423     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1424         "Cannot kick: %s", silc_get_status_message(cmd->error));
1425     COMMAND_REPLY_ERROR(cmd->error);
1426     goto out;
1427   }
1428
1429   /* Take Channel ID */
1430   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1431   if (tmp) {
1432     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1433     if (!channel_id) {
1434       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1435       goto out;
1436     }
1437
1438     /* Get the channel entry */
1439     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1440     if (!channel) {
1441       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1442       goto out;
1443     }
1444   }
1445
1446   /* Get Client ID */
1447   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1448   if (tmp) {
1449     client_id = silc_id_payload_parse_id(tmp, len, NULL);
1450     if (!client_id) {
1451       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1452       goto out;
1453     }
1454
1455     /* Get client entry */
1456     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1457     if (!client_entry) {
1458       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1459       goto out;
1460     }
1461   }
1462
1463   /* Notify application */
1464   COMMAND_REPLY((SILC_ARGS, channel, client_entry));
1465
1466  out:
1467   silc_free(channel_id);
1468   silc_free(client_id);
1469   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1470   silc_client_command_reply_free(cmd);
1471 }
1472
1473 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1474 {
1475   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1476   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1477
1478   if (cmd->error != SILC_STATUS_OK) {
1479     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1480         "%s", silc_get_status_message(cmd->error));
1481     COMMAND_REPLY_ERROR(cmd->error);
1482     goto out;
1483   }
1484
1485   /* Notify application */
1486   COMMAND_REPLY((SILC_ARGS));
1487
1488  out:
1489   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1490   silc_client_command_reply_free(cmd);
1491 }
1492
1493 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1494 {
1495   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1496   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1497
1498   if (cmd->error != SILC_STATUS_OK) {
1499     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1500         "%s", silc_get_status_message(cmd->error));
1501     COMMAND_REPLY_ERROR(cmd->error);
1502     goto out;
1503   }
1504
1505   /* Notify application */
1506   COMMAND_REPLY((SILC_ARGS));
1507
1508  out:
1509   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1510   silc_client_command_reply_free(cmd);
1511 }
1512
1513 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1514 {
1515   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1516   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1517   SilcBuffer detach;
1518
1519   if (cmd->error != SILC_STATUS_OK) {
1520     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1521         "%s", silc_get_status_message(cmd->error));
1522     COMMAND_REPLY_ERROR(cmd->error);
1523     goto out;
1524   }
1525
1526   /* Notify application */
1527   COMMAND_REPLY((SILC_ARGS));
1528
1529   /* Generate the detachment data and deliver it to the client in the
1530      detach client operation */
1531   detach = silc_client_get_detach_data(cmd->client, conn);
1532   if (detach) {
1533     cmd->client->internal->ops->detach(cmd->client, conn,
1534                                        detach->data, detach->len);
1535     silc_buffer_free(detach);
1536   }
1537
1538  out:
1539   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1540   silc_client_command_reply_free(cmd);
1541 }
1542
1543 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1544 {
1545   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1546   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1547
1548   if (cmd->error != SILC_STATUS_OK) {
1549     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1550         "%s", silc_get_status_message(cmd->error));
1551     COMMAND_REPLY_ERROR(cmd->error);
1552     goto out;
1553   }
1554
1555   /* Notify application */
1556   COMMAND_REPLY((SILC_ARGS));
1557
1558  out:
1559   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1560   silc_client_command_reply_free(cmd);
1561 }
1562
1563 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1564 {
1565   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1566   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1567   SilcChannelEntry channel;
1568   SilcChannelID *channel_id;
1569   unsigned char *tmp;
1570   SilcUInt32 len;
1571   SilcBufferStruct buf;
1572
1573   if (cmd->error != SILC_STATUS_OK) {
1574     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1575         "%s", silc_get_status_message(cmd->error));
1576     COMMAND_REPLY_ERROR(cmd->error);
1577     goto out;
1578   }
1579
1580   /* Take Channel ID */
1581   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1582   if (!tmp)
1583     goto out;
1584
1585   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1586   if (!channel_id)
1587     goto out;
1588
1589   /* Get the channel entry */
1590   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1591   if (!channel) {
1592     silc_free(channel_id);
1593     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1594     goto out;
1595   }
1596
1597   /* Get the ban list */
1598   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1599   if (tmp)
1600     silc_buffer_set(&buf, tmp, len);
1601
1602   /* Notify application */
1603   COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1604
1605  out:
1606   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1607   silc_client_command_reply_free(cmd);
1608 }
1609
1610 /* Reply to LEAVE command. */
1611
1612 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1613 {
1614   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1615   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1616   SilcChannelID *channel_id;
1617   SilcChannelEntry channel = NULL;
1618   SilcChannelUser chu;
1619   unsigned char *tmp;
1620   SilcUInt32 len;
1621
1622   if (cmd->error != SILC_STATUS_OK) {
1623     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1624         "%s", silc_get_status_message(cmd->error));
1625     COMMAND_REPLY_ERROR(cmd->error);
1626     goto out;
1627   }
1628
1629   /* From protocol version 1.1 we get the channel ID of the left channel */
1630   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1631   if (tmp) {
1632     channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1633     if (!channel_id)
1634       goto out;
1635
1636     /* Get the channel entry */
1637     channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1638     if (!channel) {
1639       silc_free(channel_id);
1640       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1641       goto out;
1642     }
1643
1644     /* Remove us from this channel. */
1645     chu = silc_client_on_channel(channel, conn->local_entry);
1646     if (chu) {
1647       silc_hash_table_del(chu->client->channels, chu->channel);
1648       silc_hash_table_del(chu->channel->user_list, chu->client);
1649       silc_free(chu);
1650     }
1651
1652     silc_free(channel_id);
1653   }
1654
1655   /* Notify application */
1656   COMMAND_REPLY((SILC_ARGS, channel));
1657
1658   /* Now delete the channel. */
1659   if (channel)
1660     silc_client_del_channel(cmd->client, conn, channel);
1661
1662  out:
1663   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1664   silc_client_command_reply_free(cmd);
1665 }
1666
1667 /* Channel resolving callback for USERS command reply. */
1668
1669 static void silc_client_command_reply_users_cb(SilcClient client,
1670                                                SilcClientConnection conn,
1671                                                SilcChannelEntry *channels,
1672                                                SilcUInt32 channels_count,
1673                                                void *context)
1674 {
1675   if (!channels_count) {
1676     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1677     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1678
1679     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1680     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1681         "%s", silc_get_status_message(cmd->error));
1682     COMMAND_REPLY_ERROR(cmd->error);
1683     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1684     silc_client_command_reply_free(cmd);
1685     return;
1686   }
1687
1688   silc_client_command_reply_users(context, NULL);
1689 }
1690
1691 static int
1692 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1693                                      SilcStatus status,
1694                                      bool notify,
1695                                      bool resolve,
1696                                      SilcGetChannelCallback get_channel,
1697                                      SilcCommandCb get_clients)
1698 {
1699   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1700   SilcChannelEntry channel;
1701   SilcClientEntry client_entry;
1702   SilcChannelUser chu;
1703   SilcChannelID *channel_id = NULL;
1704   SilcBufferStruct client_id_list, client_mode_list;
1705   unsigned char *tmp;
1706   SilcUInt32 tmp_len, list_count;
1707   int i;
1708   unsigned char **res_argv = NULL;
1709   SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1710   bool wait_res = FALSE;
1711
1712   SILC_LOG_DEBUG(("Start"));
1713
1714   /* Get channel ID */
1715   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1716   if (!tmp) {
1717     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1718     goto out;
1719   }
1720   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1721   if (!channel_id) {
1722     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1723     goto out;
1724   }
1725
1726   /* Get the list count */
1727   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1728   if (!tmp) {
1729     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1730     goto out;
1731   }
1732   SILC_GET32_MSB(list_count, tmp);
1733
1734   /* Get Client ID list */
1735   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1736   if (!tmp) {
1737     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1738     goto out;
1739   }
1740   silc_buffer_set(&client_id_list, tmp, tmp_len);
1741
1742   /* Get client mode list */
1743   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1744   if (!tmp) {
1745     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1746     goto out;
1747   }
1748   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1749
1750   /* Get channel entry */
1751   channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1752   if (!channel) {
1753     /* Resolve the channel from server */
1754     silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1755                                           get_channel, cmd);
1756     silc_free(channel_id);
1757     return 1;
1758   }
1759
1760   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1761
1762   /* Cache the received Client ID's and modes. */
1763   for (i = 0; i < list_count; i++) {
1764     SilcUInt16 idp_len;
1765     SilcUInt32 mode;
1766     SilcClientID *client_id;
1767
1768     /* Client ID */
1769     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1770     idp_len += 4;
1771     client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1772     if (!client_id)
1773       continue;
1774
1775     /* Mode */
1776     SILC_GET32_MSB(mode, client_mode_list.data);
1777
1778     /* Check if we have this client cached already. */
1779     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1780     if (!client_entry || !client_entry->username || !client_entry->realname) {
1781       if (resolve) {
1782         /* No we don't have it (or it is incomplete in information), query
1783            it from the server. Assemble argument table that will be sent
1784            for the WHOIS command later. */
1785         res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1786                                 (res_argc + 1));
1787         res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1788                                      (res_argc + 1));
1789         res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1790                                       (res_argc + 1));
1791         res_argv[res_argc] = client_id_list.data;
1792         res_argv_lens[res_argc] = idp_len;
1793         res_argv_types[res_argc] = res_argc + 4;
1794         res_argc++;
1795       }
1796     } else {
1797       if (!silc_client_on_channel(channel, client_entry)) {
1798         chu = silc_calloc(1, sizeof(*chu));
1799         chu->client = client_entry;
1800         chu->mode = mode;
1801         chu->channel = channel;
1802         silc_hash_table_add(channel->user_list, client_entry, chu);
1803         silc_hash_table_add(client_entry->channels, channel, chu);
1804       }
1805     }
1806
1807     silc_free(client_id);
1808     silc_buffer_pull(&client_id_list, idp_len);
1809     silc_buffer_pull(&client_mode_list, 4);
1810   }
1811
1812   /* Query the client information from server if the list included clients
1813      that we don't know about. */
1814   if (res_argc) {
1815     SilcBuffer res_cmd;
1816
1817     /* Send the WHOIS command to server */
1818     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1819                                  silc_client_command_reply_whois_i, 0,
1820                                  ++conn->cmd_ident);
1821     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1822                                           res_argc, res_argv, res_argv_lens,
1823                                           res_argv_types, conn->cmd_ident);
1824     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1825                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1826                             TRUE);
1827
1828     /* Register pending command callback. After we've received the WHOIS
1829        command reply we will reprocess this command reply by re-calling this
1830        USERS command reply callback. */
1831     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1832                                 get_clients, cmd);
1833
1834     silc_buffer_free(res_cmd);
1835     silc_free(channel_id);
1836     silc_free(res_argv);
1837     silc_free(res_argv_lens);
1838     silc_free(res_argv_types);
1839     return 1;
1840   }
1841
1842   if (wait_res)
1843     return 1;
1844
1845   silc_buffer_push(&client_id_list, (client_id_list.data -
1846                                      client_id_list.head));
1847   silc_buffer_push(&client_mode_list, (client_mode_list.data -
1848                                        client_mode_list.head));
1849
1850   /* Notify application */
1851   if (notify)
1852     COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1853                    &client_mode_list));
1854
1855  out:
1856   silc_free(channel_id);
1857   return 0;
1858 }
1859
1860 /* Reply to USERS command. Received list of client ID's and theirs modes
1861    on the channel we requested. */
1862
1863 SILC_CLIENT_CMD_REPLY_FUNC(users)
1864 {
1865   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1866   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1867   SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1868
1869   SILC_LOG_DEBUG(("Start"));
1870
1871   if (cmd->error != SILC_STATUS_OK) {
1872     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1873         "Query failed: %s", silc_get_status_message(cmd->error));
1874     COMMAND_REPLY_ERROR(cmd->error);
1875     goto out;
1876   }
1877
1878   if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1879     if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1880       /* Do not resolve anymore. Server may be sending us some non-existent
1881          Client ID (a bug in server), and we want to show the users list
1882          anyway. */
1883       silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1884                                            silc_client_command_reply_users_cb,
1885                                            silc_client_command_reply_users);
1886       goto out;
1887     } else {
1888       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1889           "Query failed: %s", silc_get_status_message(cmd->error));
1890       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1891       goto out;
1892     }
1893   }
1894
1895   if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1896                                            silc_client_command_reply_users_cb,
1897                                            silc_client_command_reply_users))
1898     return;
1899
1900  out:
1901   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1902   silc_client_command_reply_free(cmd);
1903 }
1904
1905 /* Received command reply to GETKEY command. WE've received the remote
1906    client's public key. */
1907
1908 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1909 {
1910   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1911   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1912   SilcIDPayload idp = NULL;
1913   SilcClientID *client_id = NULL;
1914   SilcClientEntry client_entry;
1915   SilcServerID *server_id = NULL;
1916   SilcServerEntry server_entry;
1917   unsigned char *tmp;
1918   SilcUInt32 len;
1919   SilcIdType id_type;
1920   SilcPublicKey public_key = NULL;
1921
1922   SILC_LOG_DEBUG(("Start"));
1923
1924   if (cmd->error != SILC_STATUS_OK) {
1925     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1926         "%s", silc_get_status_message(cmd->error));
1927     COMMAND_REPLY_ERROR(cmd->error);
1928     goto out;
1929   }
1930
1931   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1932   if (!tmp) {
1933     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1934     goto out;
1935   }
1936   idp = silc_id_payload_parse(tmp, len);
1937   if (!idp) {
1938     COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1939     goto out;
1940   }
1941
1942   /* Get the public key payload */
1943   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1944   if (tmp) {
1945     if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1946       public_key = NULL;
1947   }
1948
1949   id_type = silc_id_payload_get_type(idp);
1950   if (id_type == SILC_ID_CLIENT) {
1951     /* Received client's public key */
1952     client_id = silc_id_payload_get_id(idp);
1953     client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1954     if (!client_entry) {
1955       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1956       goto out;
1957     }
1958
1959     /* Save fingerprint */
1960     if (!client_entry->fingerprint) {
1961       client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1962       client_entry->fingerprint_len = 20;
1963       silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1964                      client_entry->fingerprint);
1965     }
1966     if (!client_entry->public_key) {
1967       client_entry->public_key = public_key;
1968       public_key = NULL;
1969     }
1970
1971     /* Notify application */
1972     COMMAND_REPLY((SILC_ARGS, id_type, client_entry,
1973                    client_entry->public_key));
1974   } else if (id_type == SILC_ID_SERVER) {
1975     /* Received server's public key */
1976     server_id = silc_id_payload_get_id(idp);
1977     server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1978     if (!server_entry) {
1979       COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1980       goto out;
1981     }
1982
1983     /* Notify application */
1984     COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1985   }
1986
1987  out:
1988   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1989   if (idp)
1990     silc_id_payload_free(idp);
1991   if (public_key)
1992     silc_pkcs_public_key_free(public_key);
1993   silc_free(client_id);
1994   silc_free(server_id);
1995   silc_client_command_reply_free(cmd);
1996 }
1997
1998 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1999 {
2000   silc_client_command_reply_free(context);
2001 }
2002
2003
2004 /******************************************************************************
2005
2006                       Internal command reply functions
2007
2008 ******************************************************************************/
2009
2010 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
2011 {
2012   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2013   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2014
2015   COMMAND_CHECK_STATUS_I;
2016
2017   /* Save WHOIS info */
2018   silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
2019
2020   /* Pending callbacks are not executed if this was an list entry */
2021   if (cmd->status != SILC_STATUS_OK &&
2022       cmd->status != SILC_STATUS_LIST_END) {
2023     silc_client_command_reply_free(cmd);
2024     return;
2025   }
2026
2027  out:
2028   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
2029
2030  err:
2031   /* If we received notify for invalid ID we'll remove the ID if we
2032      have it cached. */
2033   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2034     SilcClientEntry client_entry;
2035     SilcUInt32 tmp_len;
2036     unsigned char *tmp =
2037       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2038                                  2, &tmp_len);
2039     if (tmp) {
2040       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
2041       if (client_id) {
2042         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2043                                                     client_id);
2044         if (client_entry)
2045           silc_client_del_client(cmd->client, conn, client_entry);
2046         silc_free(client_id);
2047       }
2048     }
2049   }
2050
2051   /* Unregister this command reply */
2052   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
2053                                  NULL, silc_client_command_reply_whois_i,
2054                                  cmd->ident);
2055
2056   silc_client_command_reply_free(cmd);
2057 }
2058
2059 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
2060 {
2061   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2062   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2063
2064   COMMAND_CHECK_STATUS_I;
2065
2066   /* Save IDENTIFY info */
2067   silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
2068
2069   /* Pending callbacks are not executed if this was an list entry */
2070   if (cmd->status != SILC_STATUS_OK &&
2071       cmd->status != SILC_STATUS_LIST_END) {
2072     silc_client_command_reply_free(cmd);
2073     return;
2074   }
2075
2076  out:
2077   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
2078
2079  err:
2080   /* If we received notify for invalid ID we'll remove the ID if we
2081      have it cached. */
2082   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2083     SilcClientEntry client_entry;
2084     SilcUInt32 tmp_len;
2085     unsigned char *tmp =
2086       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2087                                  2, &tmp_len);
2088     if (tmp) {
2089       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
2090       if (client_id) {
2091         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2092                                                     client_id);
2093         if (client_entry)
2094           silc_client_del_client(cmd->client, conn, client_entry);
2095         silc_free(client_id);
2096       }
2097     }
2098   }
2099
2100   /* Unregister this command reply */
2101   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
2102                                  NULL, silc_client_command_reply_identify_i,
2103                                  cmd->ident);
2104
2105   silc_client_command_reply_free(cmd);
2106 }
2107
2108 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2109 {
2110   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2111   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2112   unsigned char *tmp;
2113   SilcServerEntry server;
2114   SilcServerID *server_id = NULL;
2115   char *server_name, *server_info;
2116   SilcUInt32 len;
2117
2118   COMMAND_CHECK_STATUS_I;
2119
2120   /* Get server ID */
2121   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2122   if (!tmp)
2123     goto out;
2124
2125   server_id = silc_id_payload_parse_id(tmp, len, NULL);
2126   if (!server_id)
2127     goto out;
2128
2129   /* Get server name */
2130   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2131   if (!server_name)
2132     goto out;
2133
2134   /* Get server info */
2135   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2136   if (!server_info)
2137     goto out;
2138
2139   /* See whether we have this server cached. If not create it. */
2140   server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2141   if (!server) {
2142     SILC_LOG_DEBUG(("New server entry"));
2143     silc_client_add_server(cmd->client, conn, server_name, server_info,
2144                            silc_id_dup(server_id, SILC_ID_SERVER));
2145   }
2146
2147  out:
2148   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2149   silc_free(server_id);
2150  err:
2151   silc_client_command_reply_free(cmd);
2152 }
2153
2154 static void silc_client_command_reply_users_i_cb(SilcClient client,
2155                                                  SilcClientConnection conn,
2156                                                  SilcChannelEntry *channels,
2157                                                  SilcUInt32 channels_count,
2158                                                  void *context)
2159 {
2160   if (!channels_count) {
2161     SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2162     SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2163
2164     cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2165     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2166         "%s", silc_get_status_message(cmd->error));
2167     COMMAND_REPLY_ERROR(cmd->error);
2168     SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2169     silc_client_command_reply_free(cmd);
2170     return;
2171   }
2172
2173   silc_client_command_reply_users_i(context, NULL);
2174 }
2175
2176 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2177 {
2178   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2179
2180   COMMAND_CHECK_STATUS_I;
2181
2182   /* Save USERS info */
2183   if (silc_client_command_reply_users_save(
2184                                     cmd, cmd->status, FALSE, TRUE,
2185                                     silc_client_command_reply_users_i_cb,
2186                                     silc_client_command_reply_users_i))
2187     return;
2188
2189  out:
2190   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2191
2192  err:
2193   /* Unregister this command reply */
2194   silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2195                                  NULL, silc_client_command_reply_users_i,
2196                                  cmd->ident);
2197
2198   silc_client_command_reply_free(cmd);
2199 }
2200
2201 /* Private range commands, specific to this implementation (and compatible
2202    with SILC Server >= 0.9). */
2203
2204 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2205 {
2206   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2207   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2208
2209   if (cmd->error != SILC_STATUS_OK) {
2210     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2211         "%s", silc_get_status_message(cmd->error));
2212     COMMAND_REPLY_ERROR(cmd->error);
2213     goto out;
2214   }
2215
2216   /* Notify application */
2217   COMMAND_REPLY((SILC_ARGS));
2218
2219  out:
2220   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2221   silc_client_command_reply_free(cmd);
2222 }
2223
2224 SILC_CLIENT_CMD_REPLY_FUNC(close)
2225 {
2226   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2227   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2228
2229   if (cmd->error != SILC_STATUS_OK) {
2230     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2231         "%s", silc_get_status_message(cmd->error));
2232     COMMAND_REPLY_ERROR(cmd->error);
2233     goto out;
2234   }
2235
2236   /* Notify application */
2237   COMMAND_REPLY((SILC_ARGS));
2238
2239  out:
2240   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2241   silc_client_command_reply_free(cmd);
2242 }
2243
2244 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2245 {
2246   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2247   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2248
2249   if (cmd->error != SILC_STATUS_OK) {
2250     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2251         "%s", silc_get_status_message(cmd->error));
2252     COMMAND_REPLY_ERROR(cmd->error);
2253     goto out;
2254   }
2255
2256   /* Notify application */
2257   COMMAND_REPLY((SILC_ARGS));
2258
2259  out:
2260   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2261   silc_client_command_reply_free(cmd);
2262 }