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