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