updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
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 /* $Id$ */
35
36 #include "clientlibincludes.h"
37 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(ban, BAN),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66   SILC_CLIENT_CMD_REPLY(getkey, GETKEY),
67
68   { NULL, 0 },
69 };
70
71 const SilcCommandStatusMessage silc_command_status_messages[] = {
72
73   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
74   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
75   { STAT(NO_SUCH_SERVER),    "No such server" },
76   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
77   { STAT(NO_RECIPIENT),      "No recipient given" },
78   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
79   { STAT(WILDCARDS),         "Unknown command" },
80   { STAT(NO_CLIENT_ID),      "No Client ID given" },
81   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
82   { STAT(NO_SERVER_ID),      "No Server ID given" },
83   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
84   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
85   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
86   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
87   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
88   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
89   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
90   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
91   { STAT(NOT_REGISTERED),    "You have not registered" },
92   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
93   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
94   { STAT(PERM_DENIED),       "Permission denied" },
95   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
96   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
97   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
98   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
99   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
100   { STAT(UNKNOWN_MODE),    "Unknown mode" },
101   { STAT(NOT_YOU),         "Cannot change mode for other users" },
102   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
103   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
104   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
105   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
106   { STAT(BAD_NICKNAME),    "Bad nickname" },
107   { STAT(BAD_CHANNEL),     "Bad channel name" },
108   { STAT(AUTH_FAILED),     "Authentication failed" },
109   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
110   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
111
112   { 0, NULL }
113 };
114 /* Command reply operation that is called at the end of all command replys. 
115    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
116 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
117 #define ARGS cmd->client, cmd->sock->user_data, \
118              cmd->payload, TRUE, silc_command_get(cmd->payload), status
119
120 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
121 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
122   cmd->sock->user_data, cmd->payload, FALSE, \
123   silc_command_get(cmd->payload), status)
124
125 /* All functions that call the COMMAND_CHECK_STATUS or the
126    COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
127
128 #define COMMAND_CHECK_STATUS                                              \
129 do {                                                                      \
130   SILC_LOG_DEBUG(("Start"));                                              \
131   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
132   if (status != SILC_STATUS_OK)  {                                        \
133     COMMAND_REPLY_ERROR;                                                  \
134     goto out;                                                             \
135   }                                                                       \
136 } while(0)
137
138 #define COMMAND_CHECK_STATUS_LIST                                         \
139 do {                                                                      \
140   SILC_LOG_DEBUG(("Start"));                                              \
141   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
142   if (status != SILC_STATUS_OK &&                                         \
143       status != SILC_STATUS_LIST_START &&                                 \
144       status != SILC_STATUS_LIST_ITEM &&                                  \
145       status != SILC_STATUS_LIST_END) {                                   \
146     COMMAND_REPLY_ERROR;                                                  \
147     goto out;                                                             \
148   }                                                                       \
149 } while(0)
150
151 /* Process received command reply. */
152
153 void silc_client_command_reply_process(SilcClient client,
154                                        SilcSocketConnection sock,
155                                        SilcPacketContext *packet)
156 {
157   SilcBuffer buffer = packet->buffer;
158   SilcClientCommandReply *cmd;
159   SilcClientCommandReplyContext ctx;
160   SilcCommandPayload payload;
161   SilcCommand command;
162   uint16 ident;
163
164   /* Get command reply payload from packet */
165   payload = silc_command_payload_parse(buffer);
166   if (!payload) {
167     /* Silently ignore bad reply packet */
168     SILC_LOG_DEBUG(("Bad command reply packet"));
169     return;
170   }
171   
172   /* Allocate command reply context. This must be free'd by the
173      command reply routine receiving it. */
174   ctx = silc_calloc(1, sizeof(*ctx));
175   ctx->client = client;
176   ctx->sock = sock;
177   ctx->payload = payload;
178   ctx->args = silc_command_get_args(ctx->payload);
179   ctx->packet = packet;
180   ident = silc_command_get_ident(ctx->payload);
181       
182   /* Check for pending commands and mark to be exeucted */
183   silc_client_command_pending_check(sock->user_data, ctx, 
184                                     silc_command_get(ctx->payload), ident);
185
186   /* Execute command reply */
187   command = silc_command_get(ctx->payload);
188   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
189     if (cmd->cmd == command)
190       break;
191
192   if (cmd == NULL || !cmd->cb) {
193     silc_free(ctx);
194     return;
195   }
196
197   cmd->cb(ctx, NULL);
198 }
199
200 /* Returns status message string */
201
202 char *silc_client_command_status_message(SilcCommandStatus status)
203 {
204   int i;
205
206   for (i = 0; silc_command_status_messages[i].message; i++) {
207     if (silc_command_status_messages[i].status == status)
208       break;
209   }
210
211   if (silc_command_status_messages[i].message == NULL)
212     return NULL;
213
214   return silc_command_status_messages[i].message;
215 }
216
217 /* Free command reply context and its internals. */
218
219 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
220 {
221   if (cmd) {
222     silc_command_payload_free(cmd->payload);
223     silc_free(cmd);
224   }
225 }
226
227 static void 
228 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
229                                      SilcCommandStatus status)
230 {
231   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
232   SilcClientID *client_id;
233   SilcIDCacheEntry id_cache = NULL;
234   SilcClientEntry client_entry = NULL;
235   int argc;
236   uint32 len;
237   unsigned char *id_data, *tmp;
238   char *nickname = NULL, *username = NULL;
239   char *realname = NULL;
240   uint32 idle = 0, mode = 0;
241   SilcBuffer channels = NULL;
242   unsigned char *fingerprint;
243   uint32 fingerprint_len;
244   
245   argc = silc_argument_get_arg_num(cmd->args);
246
247   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
248   if (!id_data) {
249     COMMAND_REPLY_ERROR;
250     return;
251   }
252   
253   client_id = silc_id_payload_parse_id(id_data, len);
254   if (!client_id) {
255     COMMAND_REPLY_ERROR;
256     return;
257   }
258   
259   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
260   username = silc_argument_get_arg_type(cmd->args, 4, &len);
261   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
262   if (!nickname || !username || !realname) {
263     COMMAND_REPLY_ERROR;
264     return;
265   }
266
267   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
268   if (tmp) {
269     channels = silc_buffer_alloc(len);
270     silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels));
271     silc_buffer_put(channels, tmp, len);
272   }
273
274   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
275   if (tmp)
276     SILC_GET32_MSB(mode, tmp);
277
278   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
279   if (tmp)
280     SILC_GET32_MSB(idle, tmp);
281
282   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
283
284   /* Check if we have this client cached already. */
285   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
286                                        NULL, NULL, 
287                                        silc_hash_client_id_compare, NULL,
288                                        &id_cache)) {
289     SILC_LOG_DEBUG(("Adding new client entry"));
290     client_entry = 
291       silc_client_add_client(cmd->client, conn, nickname, username, realname,
292                              client_id, mode);
293   } else {
294     client_entry = (SilcClientEntry)id_cache->context;
295     silc_client_update_client(cmd->client, conn, client_entry, 
296                               nickname, username, realname, mode);
297     silc_free(client_id);
298   }
299
300   if (fingerprint && !client_entry->fingerprint) {
301     client_entry->fingerprint = 
302       silc_calloc(fingerprint_len, 
303                   sizeof(*client_entry->fingerprint));
304     memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
305     client_entry->fingerprint_len = fingerprint_len;
306   }
307
308   if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
309     client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
310
311   /* Notify application */
312   if (!cmd->callback)
313     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
314                    channels, mode, idle, fingerprint));
315
316   if (channels)
317     silc_buffer_free(channels);
318 }
319
320 /* Received reply for WHOIS command. This maybe called several times
321    for one WHOIS command as server may reply with list of results. */
322
323 SILC_CLIENT_CMD_REPLY_FUNC(whois)
324 {
325   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
326   SilcCommandStatus status;
327
328   COMMAND_CHECK_STATUS_LIST;
329
330   /* Save WHOIS info */
331   silc_client_command_reply_whois_save(cmd, status);
332
333   /* Pending callbacks are not executed if this was an list entry */
334   if (status != SILC_STATUS_OK &&
335       status != SILC_STATUS_LIST_END) {
336     silc_client_command_reply_free(cmd);
337     return;
338   }
339
340  out:
341   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
342   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
343   silc_client_command_reply_free(cmd);
344 }
345
346 /* Received reply for WHOWAS command. */
347
348 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
349 {
350   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
351   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
352   SilcCommandStatus status;
353   SilcClientID *client_id;
354   SilcIDCacheEntry id_cache = NULL;
355   SilcClientEntry client_entry = NULL;
356   uint32 len;
357   unsigned char *id_data;
358   char *nickname, *username;
359   char *realname = NULL;
360
361   COMMAND_CHECK_STATUS_LIST;
362
363   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
364   if (!id_data) {
365     COMMAND_REPLY_ERROR;
366     goto out;
367   }
368   
369   client_id = silc_id_payload_parse_id(id_data, len);
370   if (!client_id) {
371     COMMAND_REPLY_ERROR;
372     goto out;
373   }
374
375   /* Get the client entry, if exists */
376   if (silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
377                                       NULL, NULL, 
378                                       silc_hash_client_id_compare, NULL,
379                                       &id_cache)) 
380     client_entry = (SilcClientEntry)id_cache->context;
381   silc_free(client_id);
382
383   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
384   username = silc_argument_get_arg_type(cmd->args, 4, &len);
385   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
386   if (!nickname || !username) {
387     COMMAND_REPLY_ERROR;
388     goto out;
389   }
390
391   /* Notify application. We don't save any history information to any
392      cache. Just pass the data to the application for displaying on 
393      the screen. */
394   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
395
396   /* Pending callbacks are not executed if this was an list entry */
397   if (status != SILC_STATUS_OK &&
398       status != SILC_STATUS_LIST_END) {
399     silc_client_command_reply_free(cmd);
400     return;
401   }
402
403  out:
404   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
405   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
406   silc_client_command_reply_free(cmd);
407 }
408
409 static void 
410 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
411                                         SilcCommandStatus status)
412 {
413   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
414   SilcClient client = cmd->client;
415   SilcClientID *client_id = NULL;
416   SilcServerID *server_id = NULL;
417   SilcChannelID *channel_id = NULL;
418   SilcIDCacheEntry id_cache = NULL;
419   SilcClientEntry client_entry;
420   SilcServerEntry server_entry;
421   SilcChannelEntry channel_entry;
422   int argc;
423   uint32 len;
424   unsigned char *id_data;
425   char *name = NULL, *info = NULL;
426   SilcIDPayload idp = NULL;
427   SilcIdType id_type;
428   
429   argc = silc_argument_get_arg_num(cmd->args);
430
431   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
432   if (!id_data) {
433     COMMAND_REPLY_ERROR;
434     return;
435   }
436   idp = silc_id_payload_parse_data(id_data, len);
437   if (!idp) {
438     COMMAND_REPLY_ERROR;
439     return;
440   }
441
442   name = silc_argument_get_arg_type(cmd->args, 3, &len);
443   info = silc_argument_get_arg_type(cmd->args, 4, &len);
444
445   id_type = silc_id_payload_get_type(idp);
446
447   switch (id_type) {
448   case SILC_ID_CLIENT:
449     client_id = silc_id_payload_get_id(idp);
450
451     SILC_LOG_DEBUG(("Received client information"));
452
453     /* Check if we have this client cached already. */
454     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
455                                          (void *)client_id, 
456                                          NULL, NULL, 
457                                          silc_hash_client_id_compare, NULL,
458                                          &id_cache)) {
459       SILC_LOG_DEBUG(("Adding new client entry"));
460       client_entry = 
461         silc_client_add_client(cmd->client, conn, name, info, NULL,
462                                silc_id_dup(client_id, id_type), 0);
463     } else {
464       client_entry = (SilcClientEntry)id_cache->context;
465       silc_client_update_client(cmd->client, conn, client_entry, 
466                                 name, info, NULL, 0);
467     }
468
469     if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
470       client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
471
472     /* Notify application */
473     COMMAND_REPLY((ARGS, client_entry, name, info));
474     break;
475
476   case SILC_ID_SERVER:
477     server_id = silc_id_payload_get_id(idp);
478
479     SILC_LOG_DEBUG(("Received server information"));
480
481     /* Check if we have this server cached already. */
482     if (!silc_idcache_find_by_id_one(conn->server_cache, 
483                                      (void *)server_id, &id_cache)) {
484       SILC_LOG_DEBUG(("Adding new server entry"));
485       
486       server_entry = silc_calloc(1, sizeof(*server_entry));
487       server_entry->server_id = silc_id_dup(server_id, id_type);
488       if (name)
489         server_entry->server_name = strdup(name);
490       if (info)
491         server_entry->server_info = strdup(info);
492       
493       /* Add server to cache */
494       silc_idcache_add(conn->server_cache, server_entry->server_name,
495                        server_entry->server_id, (void *)server_entry, FALSE);
496     } else {
497       server_entry = (SilcServerEntry)id_cache->context;
498     }
499
500     /* Notify application */
501     COMMAND_REPLY((ARGS, server_entry, name, info));
502     break;
503
504   case SILC_ID_CHANNEL:
505     channel_id = silc_id_payload_get_id(idp);
506
507     SILC_LOG_DEBUG(("Received channel information"));
508
509     /* Check if we have this channel cached already. */
510     if (!silc_idcache_find_by_id_one(conn->channel_cache, 
511                                      (void *)channel_id, &id_cache)) {
512       if (!name)
513         break;
514
515       SILC_LOG_DEBUG(("Adding new channel entry"));
516       channel_entry = silc_client_new_channel_id(client, conn->sock, 
517                                                  strdup(name), 0, idp);
518     } else {
519       channel_entry = (SilcChannelEntry)id_cache->context;
520     }
521
522     /* Notify application */
523     COMMAND_REPLY((ARGS, channel_entry, name, info));
524     break;
525   }
526
527   silc_id_payload_free(idp);
528   silc_free(client_id);
529   silc_free(server_id);
530   silc_free(channel_id);
531 }
532
533 /* Received reply for IDENTIFY command. This maybe called several times
534    for one IDENTIFY command as server may reply with list of results. 
535    This is totally silent and does not print anything on screen. */
536
537 SILC_CLIENT_CMD_REPLY_FUNC(identify)
538 {
539   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
540   SilcCommandStatus status;
541
542   COMMAND_CHECK_STATUS_LIST;
543
544   /* Save IDENTIFY info */
545   silc_client_command_reply_identify_save(cmd, status);
546
547   /* Pending callbacks are not executed if this was an list entry */
548   if (status != SILC_STATUS_OK &&
549       status != SILC_STATUS_LIST_END) {
550     silc_client_command_reply_free(cmd);
551     return;
552   }
553
554  out:
555   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
556   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
557   silc_client_command_reply_free(cmd);
558 }
559
560 /* Received reply for command NICK. If everything went without errors
561    we just received our new Client ID. */
562
563 SILC_CLIENT_CMD_REPLY_FUNC(nick)
564 {
565   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
566   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
567   SilcCommandStatus status;
568   SilcIDPayload idp;
569   unsigned char *tmp;
570   uint32 argc, len;
571
572   SILC_LOG_DEBUG(("Start"));
573
574   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
575   if (status != SILC_STATUS_OK) {
576     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
577                           "Cannot set nickname: %s", 
578                           silc_client_command_status_message(status));
579     COMMAND_REPLY_ERROR;
580     goto out;
581   }
582
583   argc = silc_argument_get_arg_num(cmd->args);
584   if (argc < 2 || argc > 2) {
585     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
586                           "Cannot set nickname: bad reply to command");
587     COMMAND_REPLY_ERROR;
588     goto out;
589   }
590
591   /* Take received Client ID */
592   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
593   idp = silc_id_payload_parse_data(tmp, len);
594   if (!idp) {
595     COMMAND_REPLY_ERROR;
596     goto out;
597   }
598   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
599     
600   /* Notify application */
601   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
602   COMMAND_REPLY((ARGS, conn->local_entry));
603   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
604   silc_client_command_reply_free(cmd);
605   return;
606
607  out:
608   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
609   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
610   silc_client_command_reply_free(cmd);
611 }
612
613 /* Received reply to the LIST command. */
614
615 SILC_CLIENT_CMD_REPLY_FUNC(list)
616 {
617   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
618   SilcCommandStatus status;
619   unsigned char *tmp, *name, *topic;
620   uint32 usercount = 0;
621
622   COMMAND_CHECK_STATUS_LIST;
623
624   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
625   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
626   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
627   if (tmp)
628     SILC_GET32_MSB(usercount, tmp);
629
630   /* Notify application */
631   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
632
633   /* Pending callbacks are not executed if this was an list entry */
634   if (status != SILC_STATUS_OK &&
635       status != SILC_STATUS_LIST_END) {
636     silc_client_command_reply_free(cmd);
637     return;
638   }
639
640  out:
641   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
642   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
643   silc_client_command_reply_free(cmd);
644 }
645
646 /* Received reply to topic command. */
647
648 SILC_CLIENT_CMD_REPLY_FUNC(topic)
649 {
650   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
651   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
652   SilcCommandStatus status;
653   SilcChannelEntry channel;
654   SilcChannelID *channel_id = NULL;
655   SilcIDCacheEntry id_cache = NULL;
656   unsigned char *tmp;
657   char *topic;
658   uint32 argc, len;
659
660   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
661   if (status != SILC_STATUS_OK) {
662     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
663              "%s", silc_client_command_status_message(status));
664     COMMAND_REPLY_ERROR;
665     goto out;
666   }
667
668   argc = silc_argument_get_arg_num(cmd->args);
669   if (argc < 1 || argc > 3) {
670     COMMAND_REPLY_ERROR;
671     goto out;
672   }
673
674   /* Take Channel ID */
675   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
676   if (!tmp)
677     goto out;
678
679   /* Take topic */
680   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
681   if (!topic)
682     goto out;
683
684   channel_id = silc_id_payload_parse_id(tmp, len);
685   if (!channel_id)
686     goto out;
687
688   /* Get the channel entry */
689   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
690                                    &id_cache)) {
691     silc_free(channel_id);
692     COMMAND_REPLY_ERROR;
693     goto out;
694   }
695   
696   channel = (SilcChannelEntry)id_cache->context;
697
698   /* Notify application */
699   COMMAND_REPLY((ARGS, channel, topic));
700
701  out:
702   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
703   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
704   silc_client_command_reply_free(cmd);
705 }
706
707 /* Received reply to invite command. */
708
709 SILC_CLIENT_CMD_REPLY_FUNC(invite)
710 {
711   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
712   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
713   SilcCommandStatus status;
714   SilcChannelEntry channel;
715   SilcChannelID *channel_id;
716   SilcIDCacheEntry id_cache;
717   unsigned char *tmp;
718   uint32 len;
719
720   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
721   SILC_GET16_MSB(status, tmp);
722   if (status != SILC_STATUS_OK) {
723     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
724              "%s", silc_client_command_status_message(status));
725     COMMAND_REPLY_ERROR;
726     goto out;
727   }
728
729   /* Take Channel ID */
730   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
731   if (!tmp)
732     goto out;
733
734   channel_id = silc_id_payload_parse_id(tmp, len);
735   if (!channel_id)
736     goto out;
737
738   /* Get the channel entry */
739   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
740                                    &id_cache)) {
741     silc_free(channel_id);
742     COMMAND_REPLY_ERROR;
743     goto out;
744   }
745   
746   channel = (SilcChannelEntry)id_cache->context;
747
748   /* Get the invite list */
749   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
750
751   /* Notify application */
752   COMMAND_REPLY((ARGS, channel, tmp));
753
754  out:
755   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
756   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
757   silc_client_command_reply_free(cmd);
758 }
759
760 /* Received reply to the KILL command. */
761  
762 SILC_CLIENT_CMD_REPLY_FUNC(kill)
763 {
764   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
765   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
766   SilcCommandStatus status;
767
768   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
769   if (status != SILC_STATUS_OK) {
770     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
771              "%s", silc_client_command_status_message(status));
772     COMMAND_REPLY_ERROR;
773     goto out;
774   }
775
776   /* Notify application */
777   COMMAND_REPLY((ARGS));
778
779  out:
780   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
781   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
782   silc_client_command_reply_free(cmd);
783 }
784
785 /* Received reply to INFO command. We receive the server ID and some
786    information about the server user requested. */
787
788 SILC_CLIENT_CMD_REPLY_FUNC(info)
789 {
790   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
791   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
792   SilcCommandStatus status;
793   unsigned char *tmp;
794   SilcIDCacheEntry id_cache;
795   SilcServerEntry server;
796   SilcServerID *server_id = NULL;
797   char *server_name, *server_info;
798   uint32 len;
799
800   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
801   SILC_GET16_MSB(status, tmp);
802   if (status != SILC_STATUS_OK) {
803     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
804              "%s", silc_client_command_status_message(status));
805     COMMAND_REPLY_ERROR;
806     goto out;
807   }
808
809   /* Get server ID */
810   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
811   if (!tmp)
812     goto out;
813
814   server_id = silc_id_payload_parse_id(tmp, len);
815   if (!server_id)
816     goto out;
817
818   /* Get server name */
819   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
820   if (!server_name)
821     goto out;
822
823   /* Get server info */
824   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
825   if (!server_info)
826     goto out;
827
828   /* See whether we have this server cached. If not create it. */
829   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
830                                    &id_cache)) {
831     SILC_LOG_DEBUG(("New server entry"));
832
833     server = silc_calloc(1, sizeof(*server));
834     server->server_name = strdup(server_name);
835     server->server_info = strdup(server_info);
836     server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
837
838     /* Add it to the cache */
839     silc_idcache_add(conn->server_cache, server->server_name,
840                      server->server_id, (void *)server, FALSE);
841
842     if (SILC_ID_SERVER_COMPARE(server_id, conn->remote_id))
843       goto out;
844   } else {
845     server = (SilcServerEntry)id_cache->context;
846   }
847   
848   /* Notify application */
849   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
850
851  out:
852   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
853   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
854   silc_free(server_id);
855   silc_client_command_reply_free(cmd);
856 }
857
858 /* Received reply to PING command. The reply time is shown to user. */
859
860 SILC_CLIENT_CMD_REPLY_FUNC(ping)
861 {
862   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
863   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
864   SilcCommandStatus status;
865   void *id;
866   int i;
867   time_t diff, curtime;
868
869   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
870   if (status != SILC_STATUS_OK) {
871     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
872              "%s", silc_client_command_status_message(status));
873     COMMAND_REPLY_ERROR;
874     goto out;
875   }
876
877   curtime = time(NULL);
878   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
879                       cmd->packet->src_id_type);
880   if (!id || !conn->ping) {
881     COMMAND_REPLY_ERROR;
882     goto out;
883   }
884
885   for (i = 0; i < conn->ping_count; i++) {
886     if (!conn->ping[i].dest_id)
887       continue;
888     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
889       diff = curtime - conn->ping[i].start_time;
890       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
891                             "Ping reply from %s: %d second%s", 
892                             conn->ping[i].dest_name, diff, 
893                             diff == 1 ? "" : "s");
894       
895       conn->ping[i].start_time = 0;
896       silc_free(conn->ping[i].dest_id);
897       conn->ping[i].dest_id = NULL;
898       silc_free(conn->ping[i].dest_name);
899       conn->ping[i].dest_name = NULL;
900       break;
901     }
902   }
903
904   silc_free(id);
905
906   /* Notify application */
907   COMMAND_REPLY((ARGS));
908
909  out:
910   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
911   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
912   silc_client_command_reply_free(cmd);
913 }
914
915 /* Received reply for JOIN command. */
916
917 SILC_CLIENT_CMD_REPLY_FUNC(join)
918 {
919   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
920   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
921   SilcCommandStatus status;
922   SilcIDPayload idp = NULL;
923   SilcChannelEntry channel;
924   SilcIDCacheEntry id_cache = NULL;
925   SilcChannelUser chu;
926   uint32 argc, mode, len, list_count;
927   char *topic, *tmp, *channel_name = NULL, *hmac;
928   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
929   int i;
930
931   SILC_LOG_DEBUG(("Start"));
932
933   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
934   if (status != SILC_STATUS_OK) {
935     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
936              "%s", silc_client_command_status_message(status));
937     COMMAND_REPLY_ERROR;
938     goto out;
939   }
940
941   argc = silc_argument_get_arg_num(cmd->args);
942   if (argc < 7 || argc > 14) {
943     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
944              "Cannot join channel: Bad reply packet");
945     COMMAND_REPLY_ERROR;
946     goto out;
947   }
948
949   /* Get channel name */
950   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
951   if (!tmp) {
952     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
953                           "Cannot join channel: Bad reply packet");
954     COMMAND_REPLY_ERROR;
955     goto out;
956   }
957   channel_name = strdup(tmp);
958
959   /* Get Channel ID */
960   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
961   if (!tmp) {
962     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
963                           "Cannot join channel: Bad reply packet");
964     COMMAND_REPLY_ERROR;
965     silc_free(channel_name);
966     goto out;
967   }
968   idp = silc_id_payload_parse_data(tmp, len);
969   if (!idp) {
970     COMMAND_REPLY_ERROR;
971     silc_free(channel_name);
972     goto out;
973   }
974
975   /* Get channel mode */
976   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
977   if (tmp)
978     SILC_GET32_MSB(mode, tmp);
979   else
980     mode = 0;
981
982   /* Get channel key */
983   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
984   if (tmp) {
985     keyp = silc_buffer_alloc(len);
986     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
987     silc_buffer_put(keyp, tmp, len);
988   }
989
990   /* Get topic */
991   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
992
993   /* If we have the channel entry, remove it and create a new one */
994   channel = silc_client_get_channel(cmd->client, conn, channel_name);
995   if (channel)
996     silc_client_del_channel(cmd->client, conn, channel);
997
998   /* Save received Channel ID. This actually creates the channel */
999   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
1000                                        mode, idp);
1001   silc_id_payload_free(idp);
1002
1003   conn->current_channel = channel;
1004
1005   /* Get hmac */
1006   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1007   if (hmac) {
1008     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1009       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
1010                             "Cannot join channel: Unsupported HMAC `%s'",
1011                             hmac);
1012       COMMAND_REPLY_ERROR;
1013       silc_free(channel_name);
1014       goto out;
1015     }
1016   }
1017
1018   /* Get the list count */
1019   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1020   if (!tmp)
1021     goto out;
1022   SILC_GET32_MSB(list_count, tmp);
1023
1024   /* Get Client ID list */
1025   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1026   if (!tmp)
1027     goto out;
1028
1029   client_id_list = silc_buffer_alloc(len);
1030   silc_buffer_pull_tail(client_id_list, len);
1031   silc_buffer_put(client_id_list, tmp, len);
1032
1033   /* Get client mode list */
1034   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1035   if (!tmp)
1036     goto out;
1037
1038   client_mode_list = silc_buffer_alloc(len);
1039   silc_buffer_pull_tail(client_mode_list, len);
1040   silc_buffer_put(client_mode_list, tmp, len);
1041
1042   /* Add clients we received in the reply to the channel */
1043   for (i = 0; i < list_count; i++) {
1044     uint16 idp_len;
1045     uint32 mode;
1046     SilcClientID *client_id;
1047     SilcClientEntry client_entry;
1048
1049     /* Client ID */
1050     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1051     idp_len += 4;
1052     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1053     if (!client_id)
1054       continue;
1055
1056     /* Mode */
1057     SILC_GET32_MSB(mode, client_mode_list->data);
1058
1059     /* Check if we have this client cached already. */
1060     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1061                                          (void *)client_id, 
1062                                          NULL, NULL, 
1063                                          silc_hash_client_id_compare, NULL,
1064                                          &id_cache)) {
1065       /* No, we don't have it, add entry for it. */
1066       client_entry = 
1067         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1068                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1069     } else {
1070       /* Yes, we have it already */
1071       client_entry = (SilcClientEntry)id_cache->context;
1072     }
1073
1074     /* Join the client to the channel */
1075     chu = silc_calloc(1, sizeof(*chu));
1076     chu->client = client_entry;
1077     chu->mode = mode;
1078     silc_list_add(channel->clients, chu);
1079     silc_free(client_id);
1080
1081     silc_buffer_pull(client_id_list, idp_len);
1082     silc_buffer_pull(client_mode_list, 4);
1083   }
1084   silc_buffer_push(client_id_list, client_id_list->data - 
1085                    client_id_list->head);
1086   silc_buffer_push(client_mode_list, client_mode_list->data - 
1087                    client_mode_list->head);
1088
1089   /* Save channel key */
1090   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1091     silc_client_save_channel_key(conn, keyp, channel);
1092
1093   /* Client is now joined to the channel */
1094   channel->on_channel = TRUE;
1095
1096   /* Notify application */
1097   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1098                  keyp ? keyp->head : NULL, NULL,
1099                  NULL, topic, hmac, list_count, client_id_list, 
1100                  client_mode_list));
1101
1102  out:
1103   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1104   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1105   silc_client_command_reply_free(cmd);
1106
1107   if (keyp)
1108     silc_buffer_free(keyp);
1109   if (client_id_list)
1110     silc_buffer_free(client_id_list);
1111   if (client_mode_list)
1112     silc_buffer_free(client_mode_list);
1113 }
1114
1115 /* Received reply for MOTD command */
1116
1117 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1118 {
1119   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1120   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1121   SilcCommandStatus status;
1122   uint32 argc, i;
1123   unsigned char *tmp;
1124   char *motd = NULL, *cp, line[256];
1125
1126   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1127   SILC_GET16_MSB(status, tmp);
1128   if (status != SILC_STATUS_OK) {
1129     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1130              "%s", silc_client_command_status_message(status));
1131     COMMAND_REPLY_ERROR;
1132     return;
1133   }
1134
1135   argc = silc_argument_get_arg_num(cmd->args);
1136   if (argc > 3) {
1137     COMMAND_REPLY_ERROR;
1138     goto out;
1139   }
1140
1141   if (argc == 3) {
1142     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1143     if (!motd) {
1144       COMMAND_REPLY_ERROR;
1145       goto out;
1146     }
1147
1148     i = 0;
1149     cp = motd;
1150     while(cp[i] != 0) {
1151       if (cp[i++] == '\n') {
1152         memset(line, 0, sizeof(line));
1153         strncat(line, cp, i - 1);
1154         cp += i;
1155         
1156         if (i == 2)
1157           line[0] = ' ';
1158         
1159         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1160                               "%s", line);
1161         
1162         if (!strlen(cp))
1163           break;
1164         i = 0;
1165       }
1166     }
1167   }
1168
1169   /* Notify application */
1170   COMMAND_REPLY((ARGS, motd));
1171
1172  out:
1173   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1174   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1175   silc_client_command_reply_free(cmd);
1176 }
1177
1178 /* Received reply tot he UMODE command. Save the current user mode */
1179
1180 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1181 {
1182   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1183   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1184   SilcCommandStatus status;
1185   unsigned char *tmp;
1186   uint32 mode;
1187
1188   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1189   SILC_GET16_MSB(status, tmp);
1190   if (status != SILC_STATUS_OK) {
1191     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1192              "%s", silc_client_command_status_message(status));
1193     COMMAND_REPLY_ERROR;
1194     goto out;
1195   }
1196
1197   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1198   if (!tmp) {
1199     COMMAND_REPLY_ERROR;
1200     goto out;
1201   }
1202
1203   SILC_GET32_MSB(mode, tmp);
1204   conn->local_entry->mode = mode;
1205
1206   /* Notify application */
1207   COMMAND_REPLY((ARGS, mode));
1208
1209  out:
1210   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1211   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1212   silc_client_command_reply_free(cmd);
1213 }
1214
1215 /* Received reply for CMODE command. */
1216
1217 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1218 {
1219   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1220   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1221   SilcCommandStatus status;
1222   unsigned char *tmp;
1223   uint32 mode;
1224   SilcIDCacheEntry id_cache;
1225   SilcChannelID *channel_id;
1226   SilcChannelEntry channel;
1227   uint32 len;
1228
1229   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1230   if (status != SILC_STATUS_OK) {
1231     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1232              "%s", silc_client_command_status_message(status));
1233     COMMAND_REPLY_ERROR;
1234     goto out;
1235   }
1236
1237   /* Take Channel ID */
1238   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1239   if (!tmp)
1240     goto out;
1241   channel_id = silc_id_payload_parse_id(tmp, len);
1242   if (!channel_id)
1243     goto out;
1244
1245   /* Get the channel entry */
1246   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1247                                    &id_cache)) {
1248     silc_free(channel_id);
1249     COMMAND_REPLY_ERROR;
1250     goto out;
1251   }
1252   
1253   channel = (SilcChannelEntry)id_cache->context;
1254
1255   /* Get channel mode */
1256   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1257   if (!tmp) {
1258     silc_free(channel_id);
1259     COMMAND_REPLY_ERROR;
1260     goto out;
1261   }
1262
1263   /* Save the mode */
1264   SILC_GET32_MSB(mode, tmp);
1265   channel->mode = mode;
1266
1267   /* Notify application */
1268   COMMAND_REPLY((ARGS, channel, mode));
1269
1270   silc_free(channel_id);
1271
1272  out:
1273   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1274   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1275   silc_client_command_reply_free(cmd);
1276 }
1277
1278 /* Received reply for CUMODE command */
1279
1280 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1281 {
1282   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1283   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1284   SilcCommandStatus status;
1285   SilcIDCacheEntry id_cache = NULL;
1286   SilcClientID *client_id;
1287   SilcChannelID *channel_id;
1288   SilcClientEntry client_entry;
1289   SilcChannelEntry channel;
1290   SilcChannelUser chu;
1291   unsigned char *modev, *tmp, *id;
1292   uint32 len, mode;
1293   
1294   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1295   if (status != SILC_STATUS_OK) {
1296     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1297              "%s", silc_client_command_status_message(status));
1298     COMMAND_REPLY_ERROR;
1299     goto out;
1300   }
1301   
1302   /* Get channel mode */
1303   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1304   if (!modev) {
1305     COMMAND_REPLY_ERROR;
1306     goto out;
1307   }
1308
1309   /* Take Channel ID */
1310   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1311   if (!tmp)
1312     goto out;
1313   channel_id = silc_id_payload_parse_id(tmp, len);
1314   if (!channel_id)
1315     goto out;
1316
1317   /* Get the channel entry */
1318   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1319                                    &id_cache)) {
1320     silc_free(channel_id);
1321     COMMAND_REPLY_ERROR;
1322     goto out;
1323   }
1324   
1325   channel = (SilcChannelEntry)id_cache->context;
1326
1327   /* Get Client ID */
1328   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1329   if (!id) {
1330     silc_free(channel_id);
1331     COMMAND_REPLY_ERROR;
1332     goto out;
1333   }
1334   client_id = silc_id_payload_parse_id(id, len);
1335   if (!client_id) {
1336     silc_free(channel_id);
1337     COMMAND_REPLY_ERROR;
1338     goto out;
1339   }
1340   
1341   /* Get client entry */
1342   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1343                                        NULL, NULL, 
1344                                        silc_hash_client_id_compare, NULL,
1345                                        &id_cache)) {
1346     silc_free(channel_id);
1347     silc_free(client_id);
1348     COMMAND_REPLY_ERROR;
1349     goto out;
1350   }
1351
1352   client_entry = (SilcClientEntry)id_cache->context;
1353
1354   /* Save the mode */
1355   SILC_GET32_MSB(mode, modev);
1356   silc_list_start(channel->clients);
1357   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1358     if (chu->client == client_entry) {
1359       chu->mode = mode;
1360       break;
1361     }
1362   }
1363
1364   /* Notify application */
1365   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1366   silc_free(client_id);
1367   silc_free(channel_id);
1368   
1369  out:
1370   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1371   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1372   silc_client_command_reply_free(cmd);
1373 }
1374
1375 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1376 {
1377   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1378   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1379   SilcCommandStatus status;
1380   unsigned char *tmp;
1381
1382   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1383   SILC_GET16_MSB(status, tmp);
1384   if (status != SILC_STATUS_OK) {
1385     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1386              "%s", silc_client_command_status_message(status));
1387     COMMAND_REPLY_ERROR;
1388     goto out;
1389   }
1390
1391   /* Notify application */
1392   COMMAND_REPLY((ARGS));
1393
1394  out:
1395   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1396   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1397   silc_client_command_reply_free(cmd);
1398 }
1399
1400 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1401 {
1402   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1403   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1404   SilcCommandStatus status;
1405   unsigned char *tmp;
1406
1407   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1408   SILC_GET16_MSB(status, tmp);
1409   if (status != SILC_STATUS_OK) {
1410     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1411              "%s", silc_client_command_status_message(status));
1412     COMMAND_REPLY_ERROR;
1413     goto out;
1414   }
1415
1416   /* Notify application */
1417   COMMAND_REPLY((ARGS));
1418
1419  out:
1420   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1421   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1422   silc_client_command_reply_free(cmd);
1423 }
1424
1425 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1426 {
1427   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1428   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1429   SilcCommandStatus status;
1430   unsigned char *tmp;
1431
1432   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1433   SILC_GET16_MSB(status, tmp);
1434   if (status != SILC_STATUS_OK) {
1435     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1436              "%s", silc_client_command_status_message(status));
1437     COMMAND_REPLY_ERROR;
1438     goto out;
1439   }
1440
1441   /* Notify application */
1442   COMMAND_REPLY((ARGS));
1443
1444  out:
1445   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1446   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1447   silc_client_command_reply_free(cmd);
1448 }
1449
1450 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1451 {
1452   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1453   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1454   SilcCommandStatus status;
1455   unsigned char *tmp;
1456
1457   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1458   SILC_GET16_MSB(status, tmp);
1459   if (status != SILC_STATUS_OK) {
1460     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1461              "%s", silc_client_command_status_message(status));
1462     COMMAND_REPLY_ERROR;
1463     goto out;
1464   }
1465
1466   /* Notify application */
1467   COMMAND_REPLY((ARGS));
1468
1469  out:
1470   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1471   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1472   silc_client_command_reply_free(cmd);
1473 }
1474
1475 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1476 {
1477   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1478   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1479   SilcCommandStatus status;
1480   SilcIDCacheEntry id_cache = NULL;
1481   SilcChannelEntry channel;
1482   SilcChannelID *channel_id;
1483   unsigned char *tmp;
1484   uint32 len;
1485
1486   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1487   SILC_GET16_MSB(status, tmp);
1488   if (status != SILC_STATUS_OK) {
1489     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1490              "%s", silc_client_command_status_message(status));
1491     COMMAND_REPLY_ERROR;
1492     goto out;
1493   }
1494
1495   /* Take Channel ID */
1496   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1497   if (!tmp)
1498     goto out;
1499
1500   channel_id = silc_id_payload_parse_id(tmp, len);
1501   if (!channel_id)
1502     goto out;
1503
1504   /* Get the channel entry */
1505   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1506                                    &id_cache)) {
1507     silc_free(channel_id);
1508     COMMAND_REPLY_ERROR;
1509     goto out;
1510   }
1511   
1512   channel = (SilcChannelEntry)id_cache->context;
1513
1514   /* Get the ban list */
1515   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1516
1517   /* Notify application */
1518   COMMAND_REPLY((ARGS, channel, tmp));
1519
1520  out:
1521   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1522   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1523   silc_client_command_reply_free(cmd);
1524 }
1525
1526 SILC_CLIENT_CMD_REPLY_FUNC(close)
1527 {
1528   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1529   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1530   SilcCommandStatus status;
1531   unsigned char *tmp;
1532
1533   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1534   SILC_GET16_MSB(status, tmp);
1535   if (status != SILC_STATUS_OK) {
1536     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1537              "%s", silc_client_command_status_message(status));
1538     COMMAND_REPLY_ERROR;
1539     goto out;
1540   }
1541
1542   /* Notify application */
1543   COMMAND_REPLY((ARGS));
1544
1545  out:
1546   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1547   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1548   silc_client_command_reply_free(cmd);
1549 }
1550  
1551 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1552 {
1553   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1554   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1555   SilcCommandStatus status;
1556   unsigned char *tmp;
1557
1558   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1559   SILC_GET16_MSB(status, tmp);
1560   if (status != SILC_STATUS_OK) {
1561     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1562              "%s", silc_client_command_status_message(status));
1563     COMMAND_REPLY_ERROR;
1564     goto out;
1565   }
1566
1567   /* Notify application */
1568   COMMAND_REPLY((ARGS));
1569
1570  out:
1571   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1572   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1573   silc_client_command_reply_free(cmd);
1574 }
1575  
1576 /* Reply to LEAVE command. */
1577
1578 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1579 {
1580   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1581   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1582   SilcCommandStatus status;
1583   unsigned char *tmp;
1584
1585   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1586   SILC_GET16_MSB(status, tmp);
1587   if (status != SILC_STATUS_OK) {
1588     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1589              "%s", silc_client_command_status_message(status));
1590     COMMAND_REPLY_ERROR;
1591     goto out;
1592   }
1593
1594   /* Notify application */
1595   COMMAND_REPLY((ARGS));
1596
1597  out:
1598   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1599   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1600   silc_client_command_reply_free(cmd);
1601 }
1602
1603 /* Reply to USERS command. Received list of client ID's and theirs modes
1604    on the channel we requested. */
1605
1606 SILC_CLIENT_CMD_REPLY_FUNC(users)
1607 {
1608   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1609   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1610   SilcCommandStatus status;
1611   SilcIDCacheEntry id_cache = NULL;
1612   SilcChannelEntry channel;
1613   SilcChannelUser chu;
1614   SilcChannelID *channel_id = NULL;
1615   SilcBuffer client_id_list = NULL;
1616   SilcBuffer client_mode_list = NULL;
1617   unsigned char *tmp;
1618   uint32 tmp_len, list_count;
1619   int i;
1620   unsigned char **res_argv = NULL;
1621   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1622
1623   SILC_LOG_DEBUG(("Start"));
1624
1625   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1626   SILC_GET16_MSB(status, tmp);
1627   if (status != SILC_STATUS_OK) {
1628     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1629              "%s", silc_client_command_status_message(status));
1630     COMMAND_REPLY_ERROR;
1631     goto out;
1632   }
1633
1634   /* Get channel ID */
1635   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1636   if (!tmp) {
1637     COMMAND_REPLY_ERROR;
1638     goto out;
1639   }
1640   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1641   if (!channel_id) {
1642     COMMAND_REPLY_ERROR;
1643     goto out;
1644   }
1645   
1646   /* Get the list count */
1647   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1648   if (!tmp) {
1649     COMMAND_REPLY_ERROR;
1650     goto out;
1651   }
1652   SILC_GET32_MSB(list_count, tmp);
1653
1654   /* Get Client ID list */
1655   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1656   if (!tmp) {
1657     COMMAND_REPLY_ERROR;
1658     goto out;
1659   }
1660
1661   client_id_list = silc_buffer_alloc(tmp_len);
1662   silc_buffer_pull_tail(client_id_list, tmp_len);
1663   silc_buffer_put(client_id_list, tmp, tmp_len);
1664
1665   /* Get client mode list */
1666   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1667   if (!tmp) {
1668     COMMAND_REPLY_ERROR;
1669     goto out;
1670   }
1671
1672   client_mode_list = silc_buffer_alloc(tmp_len);
1673   silc_buffer_pull_tail(client_mode_list, tmp_len);
1674   silc_buffer_put(client_mode_list, tmp, tmp_len);
1675
1676   /* Get channel entry */
1677   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1678                                    &id_cache)) {
1679     /* Resolve the channel from server */
1680     silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
1681     
1682     /* Register pending command callback. After we've received the channel
1683        information we will reprocess this command reply by re-calling this
1684        USERS command reply callback. */
1685     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1686                                 NULL, silc_client_command_reply_users, cmd);
1687     return;
1688   } else {
1689     channel = (SilcChannelEntry)id_cache->context;
1690   }
1691
1692   /* Remove old client list from channel. */
1693   silc_list_start(channel->clients);
1694   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1695     silc_list_del(channel->clients, chu);
1696     silc_free(chu);
1697   }
1698
1699   /* Cache the received Client ID's and modes. */
1700   for (i = 0; i < list_count; i++) {
1701     uint16 idp_len;
1702     uint32 mode;
1703     SilcClientID *client_id;
1704     SilcClientEntry client;
1705
1706     /* Client ID */
1707     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1708     idp_len += 4;
1709     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1710     if (!client_id)
1711       continue;
1712
1713     /* Mode */
1714     SILC_GET32_MSB(mode, client_mode_list->data);
1715
1716     /* Check if we have this client cached already. */
1717     id_cache = NULL;
1718     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1719                                     (void *)client_id, 
1720                                     NULL, NULL, 
1721                                     silc_hash_client_id_compare, NULL,
1722                                     &id_cache);
1723
1724     if (!id_cache || !((SilcClientEntry)id_cache->context)->username ||
1725         !((SilcClientEntry)id_cache->context)->realname) {
1726
1727       if (id_cache && id_cache->context) {
1728         SilcClientEntry client_entry = (SilcClientEntry)id_cache->context;
1729         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
1730           client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
1731           silc_buffer_pull(client_id_list, idp_len);
1732           silc_buffer_pull(client_mode_list, 4);
1733           continue;
1734         }
1735         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
1736       }
1737
1738       /* No we don't have it (or it is incomplete in information), query
1739          it from the server. Assemble argument table that will be sent
1740          for the WHOIS command later. */
1741       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1742                               (res_argc + 1));
1743       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1744                                    (res_argc + 1));
1745       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1746                                     (res_argc + 1));
1747       res_argv[res_argc] = client_id_list->data;
1748       res_argv_lens[res_argc] = idp_len;
1749       res_argv_types[res_argc] = res_argc + 3;
1750       res_argc++;
1751     } else {
1752       /* Found the client, join it to the channel */
1753       client = (SilcClientEntry)id_cache->context;
1754       chu = silc_calloc(1, sizeof(*chu));
1755       chu->client = client;
1756       chu->mode = mode;
1757       silc_list_add(channel->clients, chu);
1758
1759       silc_free(client_id);
1760       id_cache = NULL;
1761     }
1762
1763     silc_buffer_pull(client_id_list, idp_len);
1764     silc_buffer_pull(client_mode_list, 4);
1765   }
1766
1767   /* Query the client information from server if the list included clients
1768      that we don't know about. */
1769   if (res_argc) {
1770     SilcBuffer res_cmd;
1771
1772     /* Send the WHOIS command to server */
1773     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1774                                           res_argc, res_argv, res_argv_lens,
1775                                           res_argv_types, ++conn->cmd_ident);
1776     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1777                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1778                             TRUE);
1779
1780     /* Register pending command callback. After we've received the WHOIS
1781        command reply we will reprocess this command reply by re-calling this
1782        USERS command reply callback. */
1783     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1784                                 NULL, silc_client_command_reply_users, cmd);
1785
1786     silc_buffer_free(res_cmd);
1787     if (channel_id)
1788       silc_free(channel_id);
1789
1790     silc_free(res_argv);
1791     silc_free(res_argv_lens);
1792     silc_free(res_argv_types);
1793     return;
1794   }
1795
1796   /* Notify application */
1797   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1798
1799  out:
1800   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1801   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1802   silc_client_command_reply_free(cmd);
1803   silc_free(channel_id);
1804   if (client_id_list)
1805     silc_buffer_free(client_id_list);
1806   if (client_mode_list)
1807     silc_buffer_free(client_mode_list);
1808 }
1809
1810 /* Received command reply to GETKEY command. WE've received the remote
1811    client's public key. */
1812
1813 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1814 {
1815   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1816   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1817   SilcCommandStatus status;
1818   SilcIDCacheEntry id_cache;
1819   SilcIDPayload idp = NULL;
1820   SilcClientID *client_id = NULL;
1821   SilcClientEntry client_entry;
1822   SilcServerID *server_id = NULL;
1823   SilcServerEntry server_entry;
1824   SilcSKEPKType type;
1825   unsigned char *tmp, *pk;
1826   uint32 len;
1827   uint16 pk_len;
1828   SilcIdType id_type;
1829   SilcPublicKey public_key = NULL;
1830
1831   SILC_LOG_DEBUG(("Start"));
1832
1833   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1834   SILC_GET16_MSB(status, tmp);
1835   if (status != SILC_STATUS_OK) {
1836     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1837                           "%s", silc_client_command_status_message(status));
1838     COMMAND_REPLY_ERROR;
1839     goto out;
1840   }
1841
1842   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1843   if (!tmp) {
1844     COMMAND_REPLY_ERROR;
1845     goto out;
1846   }
1847   idp = silc_id_payload_parse_data(tmp, len);
1848   if (!idp) {
1849     COMMAND_REPLY_ERROR;
1850     goto out;
1851   }
1852
1853   /* Get the public key payload */
1854   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1855   if (tmp) {
1856     /* Decode the public key */
1857     SILC_GET16_MSB(pk_len, tmp);
1858     SILC_GET16_MSB(type, tmp + 2);
1859     pk = tmp + 4;
1860     
1861     if (type != SILC_SKE_PK_TYPE_SILC) {
1862       COMMAND_REPLY_ERROR;
1863       goto out;
1864     }
1865     
1866     if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) {
1867       COMMAND_REPLY_ERROR;
1868       goto out;
1869     }
1870   } 
1871    
1872   id_type = silc_id_payload_get_type(idp);
1873   if (id_type == SILC_ID_CLIENT) {
1874     /* Received client's public key */
1875     client_id = silc_id_payload_get_id(idp);
1876     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1877                                          (void *)client_id, 
1878                                          NULL, NULL, 
1879                                          silc_hash_client_id_compare, NULL,
1880                                          &id_cache)) {
1881       COMMAND_REPLY_ERROR;
1882       goto out;
1883     }
1884
1885     client_entry = (SilcClientEntry)id_cache->context;
1886
1887     /* Notify application */
1888     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1889   } else if (id_type == SILC_ID_SERVER) {
1890     /* Received server's public key */
1891     server_id = silc_id_payload_get_id(idp);
1892     if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
1893                                      &id_cache)) {
1894       COMMAND_REPLY_ERROR;
1895       goto out;
1896     }
1897
1898     server_entry = (SilcServerEntry)id_cache->context;
1899
1900     /* Notify application */
1901     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1902   }
1903
1904  out:
1905   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1906   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1907   if (idp)
1908     silc_id_payload_free(idp);
1909   if (public_key)
1910     silc_pkcs_public_key_free(public_key);
1911   silc_free(client_id);
1912   silc_free(server_id);
1913   silc_client_command_reply_free(cmd);
1914 }