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