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