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