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));
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   COMMAND_REPLY((ARGS, conn->local_entry));
602
603  out:
604   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
605   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
606   silc_client_command_reply_free(cmd);
607 }
608
609 /* Received reply to the LIST command. */
610
611 SILC_CLIENT_CMD_REPLY_FUNC(list)
612 {
613   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
614   SilcCommandStatus status;
615   unsigned char *tmp, *name, *topic;
616   uint32 usercount = 0;
617
618   COMMAND_CHECK_STATUS_LIST;
619
620   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
621   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
622   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
623   if (tmp)
624     SILC_GET32_MSB(usercount, tmp);
625
626   /* Notify application */
627   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
628
629   /* Pending callbacks are not executed if this was an list entry */
630   if (status != SILC_STATUS_OK &&
631       status != SILC_STATUS_LIST_END) {
632     silc_client_command_reply_free(cmd);
633     return;
634   }
635
636  out:
637   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
638   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
639   silc_client_command_reply_free(cmd);
640 }
641
642 /* Received reply to topic command. */
643
644 SILC_CLIENT_CMD_REPLY_FUNC(topic)
645 {
646   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
647   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
648   SilcCommandStatus status;
649   SilcChannelEntry channel;
650   SilcChannelID *channel_id = NULL;
651   SilcIDCacheEntry id_cache = NULL;
652   unsigned char *tmp;
653   char *topic;
654   uint32 argc, len;
655
656   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
657   if (status != SILC_STATUS_OK) {
658     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
659              "%s", silc_client_command_status_message(status));
660     COMMAND_REPLY_ERROR;
661     goto out;
662   }
663
664   argc = silc_argument_get_arg_num(cmd->args);
665   if (argc < 1 || argc > 3) {
666     COMMAND_REPLY_ERROR;
667     goto out;
668   }
669
670   /* Take Channel ID */
671   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
672   if (!tmp)
673     goto out;
674
675   /* Take topic */
676   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
677   if (!topic)
678     goto out;
679
680   channel_id = silc_id_payload_parse_id(tmp, len);
681   if (!channel_id)
682     goto out;
683
684   /* Get the channel entry */
685   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
686                                    &id_cache)) {
687     silc_free(channel_id);
688     COMMAND_REPLY_ERROR;
689     goto out;
690   }
691   
692   channel = (SilcChannelEntry)id_cache->context;
693
694   /* Notify application */
695   COMMAND_REPLY((ARGS, channel, topic));
696
697  out:
698   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
699   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
700   silc_client_command_reply_free(cmd);
701 }
702
703 /* Received reply to invite command. */
704
705 SILC_CLIENT_CMD_REPLY_FUNC(invite)
706 {
707   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
708   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
709   SilcCommandStatus status;
710   SilcChannelEntry channel;
711   SilcChannelID *channel_id;
712   SilcIDCacheEntry id_cache;
713   unsigned char *tmp;
714   uint32 len;
715
716   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
717   SILC_GET16_MSB(status, tmp);
718   if (status != SILC_STATUS_OK) {
719     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
720              "%s", silc_client_command_status_message(status));
721     COMMAND_REPLY_ERROR;
722     goto out;
723   }
724
725   /* Take Channel ID */
726   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
727   if (!tmp)
728     goto out;
729
730   channel_id = silc_id_payload_parse_id(tmp, len);
731   if (!channel_id)
732     goto out;
733
734   /* Get the channel entry */
735   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
736                                    &id_cache)) {
737     silc_free(channel_id);
738     COMMAND_REPLY_ERROR;
739     goto out;
740   }
741   
742   channel = (SilcChannelEntry)id_cache->context;
743
744   /* Get the invite list */
745   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
746
747   /* Notify application */
748   COMMAND_REPLY((ARGS, channel, tmp));
749
750  out:
751   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
752   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
753   silc_client_command_reply_free(cmd);
754 }
755
756 /* Received reply to the KILL command. */
757  
758 SILC_CLIENT_CMD_REPLY_FUNC(kill)
759 {
760   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
761   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
762   SilcCommandStatus status;
763
764   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
765   if (status != SILC_STATUS_OK) {
766     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
767              "%s", silc_client_command_status_message(status));
768     COMMAND_REPLY_ERROR;
769     goto out;
770   }
771
772   /* Notify application */
773   COMMAND_REPLY((ARGS));
774
775  out:
776   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
777   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
778   silc_client_command_reply_free(cmd);
779 }
780
781 /* Received reply to INFO command. We receive the server ID and some
782    information about the server user requested. */
783
784 SILC_CLIENT_CMD_REPLY_FUNC(info)
785 {
786   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
787   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
788   SilcCommandStatus status;
789   unsigned char *tmp;
790   SilcIDCacheEntry id_cache;
791   SilcServerEntry server;
792   SilcServerID *server_id = NULL;
793   char *server_name, *server_info;
794   uint32 len;
795
796   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
797   SILC_GET16_MSB(status, tmp);
798   if (status != SILC_STATUS_OK) {
799     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
800              "%s", silc_client_command_status_message(status));
801     COMMAND_REPLY_ERROR;
802     goto out;
803   }
804
805   /* Get server ID */
806   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
807   if (!tmp)
808     goto out;
809
810   server_id = silc_id_payload_parse_id(tmp, len);
811   if (!server_id)
812     goto out;
813
814   /* Get server name */
815   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
816   if (!server_name)
817     goto out;
818
819   /* Get server info */
820   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
821   if (!server_info)
822     goto out;
823
824   /* See whether we have this server cached. If not create it. */
825   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
826                                    &id_cache)) {
827     SILC_LOG_DEBUG(("New server entry"));
828
829     server = silc_calloc(1, sizeof(*server));
830     server->server_name = strdup(server_name);
831     server->server_info = strdup(server_info);
832     server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
833
834     /* Add it to the cache */
835     silc_idcache_add(conn->server_cache, server->server_name,
836                      server->server_id, (void *)server, FALSE);
837   } else {
838     server = (SilcServerEntry)id_cache->context;
839   }
840   
841   /* Notify application */
842   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
843
844  out:
845   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
846   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
847   silc_free(server_id);
848   silc_client_command_reply_free(cmd);
849 }
850
851 /* Received reply to PING command. The reply time is shown to user. */
852
853 SILC_CLIENT_CMD_REPLY_FUNC(ping)
854 {
855   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
856   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
857   SilcCommandStatus status;
858   void *id;
859   int i;
860   time_t diff, curtime;
861
862   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
863   if (status != SILC_STATUS_OK) {
864     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
865              "%s", silc_client_command_status_message(status));
866     COMMAND_REPLY_ERROR;
867     goto out;
868   }
869
870   curtime = time(NULL);
871   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
872                       cmd->packet->src_id_type);
873   if (!id) {
874     COMMAND_REPLY_ERROR;
875     goto out;
876   }
877
878   for (i = 0; i < conn->ping_count; i++) {
879     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
880       diff = curtime - conn->ping[i].start_time;
881       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
882                             "Ping reply from %s: %d second%s", 
883                             conn->ping[i].dest_name, diff, 
884                             diff == 1 ? "" : "s");
885       
886       conn->ping[i].start_time = 0;
887       silc_free(conn->ping[i].dest_id);
888       conn->ping[i].dest_id = NULL;
889       silc_free(conn->ping[i].dest_name);
890       conn->ping[i].dest_name = NULL;
891       break;
892     }
893   }
894
895   silc_free(id);
896
897   /* Notify application */
898   COMMAND_REPLY((ARGS));
899
900  out:
901   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
902   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
903   silc_client_command_reply_free(cmd);
904 }
905
906 /* Received reply for JOIN command. */
907
908 SILC_CLIENT_CMD_REPLY_FUNC(join)
909 {
910   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
911   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
912   SilcCommandStatus status;
913   SilcIDPayload idp = NULL;
914   SilcChannelEntry channel;
915   SilcIDCacheEntry id_cache = NULL;
916   SilcChannelUser chu;
917   uint32 argc, mode, len, list_count;
918   char *topic, *tmp, *channel_name = NULL, *hmac;
919   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
920   int i;
921
922   SILC_LOG_DEBUG(("Start"));
923
924   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
925   if (status != SILC_STATUS_OK) {
926     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
927              "%s", silc_client_command_status_message(status));
928     COMMAND_REPLY_ERROR;
929     goto out;
930   }
931
932   argc = silc_argument_get_arg_num(cmd->args);
933   if (argc < 7 || argc > 14) {
934     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
935              "Cannot join channel: Bad reply packet");
936     COMMAND_REPLY_ERROR;
937     goto out;
938   }
939
940   /* Get channel name */
941   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
942   if (!tmp) {
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   channel_name = strdup(tmp);
949
950   /* Get Channel ID */
951   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
952   if (!tmp) {
953     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
954                           "Cannot join channel: Bad reply packet");
955     COMMAND_REPLY_ERROR;
956     silc_free(channel_name);
957     goto out;
958   }
959   idp = silc_id_payload_parse_data(tmp, len);
960   if (!idp) {
961     COMMAND_REPLY_ERROR;
962     silc_free(channel_name);
963     goto out;
964   }
965
966   /* Get channel mode */
967   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
968   if (tmp)
969     SILC_GET32_MSB(mode, tmp);
970   else
971     mode = 0;
972
973   /* Get channel key */
974   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
975   if (tmp) {
976     keyp = silc_buffer_alloc(len);
977     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
978     silc_buffer_put(keyp, tmp, len);
979   }
980
981   /* Get topic */
982   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
983
984   /* If we have the channel entry, remove it and create a new one */
985   channel = silc_client_get_channel(cmd->client, conn, channel_name);
986   if (channel)
987     silc_client_del_channel(cmd->client, conn, channel);
988
989   /* Save received Channel ID. This actually creates the channel */
990   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
991                                        mode, idp);
992   silc_id_payload_free(idp);
993
994   conn->current_channel = channel;
995
996   /* Get hmac */
997   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
998   if (hmac) {
999     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1000       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
1001                             "Cannot join channel: Unsupported HMAC `%s'",
1002                             hmac);
1003       COMMAND_REPLY_ERROR;
1004       silc_free(channel_name);
1005       goto out;
1006     }
1007   }
1008
1009   /* Get the list count */
1010   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1011   if (!tmp)
1012     goto out;
1013   SILC_GET32_MSB(list_count, tmp);
1014
1015   /* Get Client ID list */
1016   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1017   if (!tmp)
1018     goto out;
1019
1020   client_id_list = silc_buffer_alloc(len);
1021   silc_buffer_pull_tail(client_id_list, len);
1022   silc_buffer_put(client_id_list, tmp, len);
1023
1024   /* Get client mode list */
1025   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1026   if (!tmp)
1027     goto out;
1028
1029   client_mode_list = silc_buffer_alloc(len);
1030   silc_buffer_pull_tail(client_mode_list, len);
1031   silc_buffer_put(client_mode_list, tmp, len);
1032
1033   /* Add clients we received in the reply to the channel */
1034   for (i = 0; i < list_count; i++) {
1035     uint16 idp_len;
1036     uint32 mode;
1037     SilcClientID *client_id;
1038     SilcClientEntry client_entry;
1039
1040     /* Client ID */
1041     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1042     idp_len += 4;
1043     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1044     if (!client_id)
1045       continue;
1046
1047     /* Mode */
1048     SILC_GET32_MSB(mode, client_mode_list->data);
1049
1050     /* Check if we have this client cached already. */
1051     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1052                                          (void *)client_id, 
1053                                          NULL, NULL, 
1054                                          silc_hash_client_id_compare, NULL,
1055                                          &id_cache)) {
1056       /* No, we don't have it, add entry for it. */
1057       client_entry = 
1058         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1059                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1060     } else {
1061       /* Yes, we have it already */
1062       client_entry = (SilcClientEntry)id_cache->context;
1063     }
1064
1065     /* Join the client to the channel */
1066     chu = silc_calloc(1, sizeof(*chu));
1067     chu->client = client_entry;
1068     chu->mode = mode;
1069     silc_list_add(channel->clients, chu);
1070     silc_free(client_id);
1071
1072     silc_buffer_pull(client_id_list, idp_len);
1073     silc_buffer_pull(client_mode_list, 4);
1074   }
1075   silc_buffer_push(client_id_list, client_id_list->data - 
1076                    client_id_list->head);
1077   silc_buffer_push(client_mode_list, client_mode_list->data - 
1078                    client_mode_list->head);
1079
1080   /* Save channel key */
1081   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1082     silc_client_save_channel_key(conn, keyp, channel);
1083
1084   /* Client is now joined to the channel */
1085   channel->on_channel = TRUE;
1086
1087   /* Notify application */
1088   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1089                  keyp ? keyp->head : NULL, NULL,
1090                  NULL, topic, hmac, list_count, client_id_list, 
1091                  client_mode_list));
1092
1093  out:
1094   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1095   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1096   silc_client_command_reply_free(cmd);
1097
1098   if (keyp)
1099     silc_buffer_free(keyp);
1100   if (client_id_list)
1101     silc_buffer_free(client_id_list);
1102   if (client_mode_list)
1103     silc_buffer_free(client_mode_list);
1104 }
1105
1106 /* Received reply for MOTD command */
1107
1108 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1109 {
1110   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1111   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1112   SilcCommandStatus status;
1113   uint32 argc, i;
1114   unsigned char *tmp;
1115   char *motd = NULL, *cp, line[256];
1116
1117   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1118   SILC_GET16_MSB(status, tmp);
1119   if (status != SILC_STATUS_OK) {
1120     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1121              "%s", silc_client_command_status_message(status));
1122     COMMAND_REPLY_ERROR;
1123     return;
1124   }
1125
1126   argc = silc_argument_get_arg_num(cmd->args);
1127   if (argc > 3) {
1128     COMMAND_REPLY_ERROR;
1129     goto out;
1130   }
1131
1132   if (argc == 3) {
1133     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1134     if (!motd) {
1135       COMMAND_REPLY_ERROR;
1136       goto out;
1137     }
1138
1139     i = 0;
1140     cp = motd;
1141     while(cp[i] != 0) {
1142       if (cp[i++] == '\n') {
1143         memset(line, 0, sizeof(line));
1144         strncat(line, cp, i - 1);
1145         cp += i;
1146         
1147         if (i == 2)
1148           line[0] = ' ';
1149         
1150         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1151                               "%s", line);
1152         
1153         if (!strlen(cp))
1154           break;
1155         i = 0;
1156       }
1157     }
1158   }
1159
1160   /* Notify application */
1161   COMMAND_REPLY((ARGS, motd));
1162
1163  out:
1164   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1165   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1166   silc_client_command_reply_free(cmd);
1167 }
1168
1169 /* Received reply tot he UMODE command. Save the current user mode */
1170
1171 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1172 {
1173   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1174   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1175   SilcCommandStatus status;
1176   unsigned char *tmp;
1177   uint32 mode;
1178
1179   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1180   SILC_GET16_MSB(status, tmp);
1181   if (status != SILC_STATUS_OK) {
1182     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1183              "%s", silc_client_command_status_message(status));
1184     COMMAND_REPLY_ERROR;
1185     goto out;
1186   }
1187
1188   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1189   if (!tmp) {
1190     COMMAND_REPLY_ERROR;
1191     goto out;
1192   }
1193
1194   SILC_GET32_MSB(mode, tmp);
1195   conn->local_entry->mode = mode;
1196
1197   /* Notify application */
1198   COMMAND_REPLY((ARGS, mode));
1199
1200  out:
1201   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1202   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1203   silc_client_command_reply_free(cmd);
1204 }
1205
1206 /* Received reply for CMODE command. */
1207
1208 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1209 {
1210   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1211   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1212   SilcCommandStatus status;
1213   unsigned char *tmp;
1214   uint32 mode;
1215   SilcIDCacheEntry id_cache;
1216   SilcChannelID *channel_id;
1217   SilcChannelEntry channel;
1218   uint32 len;
1219
1220   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1221   if (status != SILC_STATUS_OK) {
1222     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1223              "%s", silc_client_command_status_message(status));
1224     COMMAND_REPLY_ERROR;
1225     goto out;
1226   }
1227
1228   /* Take Channel ID */
1229   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1230   if (!tmp)
1231     goto out;
1232   channel_id = silc_id_payload_parse_id(tmp, len);
1233   if (!channel_id)
1234     goto out;
1235
1236   /* Get the channel entry */
1237   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1238                                    &id_cache)) {
1239     silc_free(channel_id);
1240     COMMAND_REPLY_ERROR;
1241     goto out;
1242   }
1243   
1244   channel = (SilcChannelEntry)id_cache->context;
1245
1246   /* Get channel mode */
1247   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1248   if (!tmp) {
1249     silc_free(channel_id);
1250     COMMAND_REPLY_ERROR;
1251     goto out;
1252   }
1253
1254   /* Save the mode */
1255   SILC_GET32_MSB(mode, tmp);
1256   channel->mode = mode;
1257
1258   /* Notify application */
1259   COMMAND_REPLY((ARGS, channel, mode));
1260
1261   silc_free(channel_id);
1262
1263  out:
1264   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1265   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1266   silc_client_command_reply_free(cmd);
1267 }
1268
1269 /* Received reply for CUMODE command */
1270
1271 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1272 {
1273   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1274   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1275   SilcCommandStatus status;
1276   SilcIDCacheEntry id_cache = NULL;
1277   SilcClientID *client_id;
1278   SilcChannelID *channel_id;
1279   SilcClientEntry client_entry;
1280   SilcChannelEntry channel;
1281   SilcChannelUser chu;
1282   unsigned char *modev, *tmp, *id;
1283   uint32 len, mode;
1284   
1285   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1286   if (status != SILC_STATUS_OK) {
1287     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1288              "%s", silc_client_command_status_message(status));
1289     COMMAND_REPLY_ERROR;
1290     goto out;
1291   }
1292   
1293   /* Get channel mode */
1294   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1295   if (!modev) {
1296     COMMAND_REPLY_ERROR;
1297     goto out;
1298   }
1299
1300   /* Take Channel ID */
1301   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1302   if (!tmp)
1303     goto out;
1304   channel_id = silc_id_payload_parse_id(tmp, len);
1305   if (!channel_id)
1306     goto out;
1307
1308   /* Get the channel entry */
1309   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1310                                    &id_cache)) {
1311     silc_free(channel_id);
1312     COMMAND_REPLY_ERROR;
1313     goto out;
1314   }
1315   
1316   channel = (SilcChannelEntry)id_cache->context;
1317
1318   /* Get Client ID */
1319   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1320   if (!id) {
1321     silc_free(channel_id);
1322     COMMAND_REPLY_ERROR;
1323     goto out;
1324   }
1325   client_id = silc_id_payload_parse_id(id, len);
1326   if (!client_id) {
1327     silc_free(channel_id);
1328     COMMAND_REPLY_ERROR;
1329     goto out;
1330   }
1331   
1332   /* Get client entry */
1333   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1334                                        NULL, NULL, 
1335                                        silc_hash_client_id_compare, NULL,
1336                                        &id_cache)) {
1337     silc_free(channel_id);
1338     silc_free(client_id);
1339     COMMAND_REPLY_ERROR;
1340     goto out;
1341   }
1342
1343   client_entry = (SilcClientEntry)id_cache->context;
1344
1345   /* Save the mode */
1346   SILC_GET32_MSB(mode, modev);
1347   silc_list_start(channel->clients);
1348   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1349     if (chu->client == client_entry) {
1350       chu->mode = mode;
1351       break;
1352     }
1353   }
1354
1355   /* Notify application */
1356   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1357   silc_free(client_id);
1358   silc_free(channel_id);
1359   
1360  out:
1361   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1362   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1363   silc_client_command_reply_free(cmd);
1364 }
1365
1366 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1367 {
1368   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1369   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1370   SilcCommandStatus status;
1371   unsigned char *tmp;
1372
1373   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1374   SILC_GET16_MSB(status, tmp);
1375   if (status != SILC_STATUS_OK) {
1376     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1377              "%s", silc_client_command_status_message(status));
1378     COMMAND_REPLY_ERROR;
1379     goto out;
1380   }
1381
1382   /* Notify application */
1383   COMMAND_REPLY((ARGS));
1384
1385  out:
1386   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1387   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1388   silc_client_command_reply_free(cmd);
1389 }
1390
1391 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1392 {
1393   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1394   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1395   SilcCommandStatus status;
1396   unsigned char *tmp;
1397
1398   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1399   SILC_GET16_MSB(status, tmp);
1400   if (status != SILC_STATUS_OK) {
1401     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1402              "%s", silc_client_command_status_message(status));
1403     COMMAND_REPLY_ERROR;
1404     goto out;
1405   }
1406
1407   /* Notify application */
1408   COMMAND_REPLY((ARGS));
1409
1410  out:
1411   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1412   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1413   silc_client_command_reply_free(cmd);
1414 }
1415
1416 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1417 {
1418   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1419   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1420   SilcCommandStatus status;
1421   unsigned char *tmp;
1422
1423   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1424   SILC_GET16_MSB(status, tmp);
1425   if (status != SILC_STATUS_OK) {
1426     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1427              "%s", silc_client_command_status_message(status));
1428     COMMAND_REPLY_ERROR;
1429     goto out;
1430   }
1431
1432   /* Notify application */
1433   COMMAND_REPLY((ARGS));
1434
1435  out:
1436   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1437   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1438   silc_client_command_reply_free(cmd);
1439 }
1440
1441 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1442 {
1443   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1444   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1445   SilcCommandStatus status;
1446   unsigned char *tmp;
1447
1448   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1449   SILC_GET16_MSB(status, tmp);
1450   if (status != SILC_STATUS_OK) {
1451     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1452              "%s", silc_client_command_status_message(status));
1453     COMMAND_REPLY_ERROR;
1454     goto out;
1455   }
1456
1457   /* Notify application */
1458   COMMAND_REPLY((ARGS));
1459
1460  out:
1461   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1462   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1463   silc_client_command_reply_free(cmd);
1464 }
1465
1466 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1467 {
1468   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1469   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1470   SilcCommandStatus status;
1471   SilcIDCacheEntry id_cache = NULL;
1472   SilcChannelEntry channel;
1473   SilcChannelID *channel_id;
1474   unsigned char *tmp;
1475   uint32 len;
1476
1477   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1478   SILC_GET16_MSB(status, tmp);
1479   if (status != SILC_STATUS_OK) {
1480     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1481              "%s", silc_client_command_status_message(status));
1482     COMMAND_REPLY_ERROR;
1483     goto out;
1484   }
1485
1486   /* Take Channel ID */
1487   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1488   if (!tmp)
1489     goto out;
1490
1491   channel_id = silc_id_payload_parse_id(tmp, len);
1492   if (!channel_id)
1493     goto out;
1494
1495   /* Get the channel entry */
1496   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1497                                    &id_cache)) {
1498     silc_free(channel_id);
1499     COMMAND_REPLY_ERROR;
1500     goto out;
1501   }
1502   
1503   channel = (SilcChannelEntry)id_cache->context;
1504
1505   /* Get the ban list */
1506   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1507
1508   /* Notify application */
1509   COMMAND_REPLY((ARGS, channel, tmp));
1510
1511  out:
1512   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1513   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1514   silc_client_command_reply_free(cmd);
1515 }
1516
1517 SILC_CLIENT_CMD_REPLY_FUNC(close)
1518 {
1519   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1520   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1521   SilcCommandStatus status;
1522   unsigned char *tmp;
1523
1524   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1525   SILC_GET16_MSB(status, tmp);
1526   if (status != SILC_STATUS_OK) {
1527     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1528              "%s", silc_client_command_status_message(status));
1529     COMMAND_REPLY_ERROR;
1530     goto out;
1531   }
1532
1533   /* Notify application */
1534   COMMAND_REPLY((ARGS));
1535
1536  out:
1537   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1538   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1539   silc_client_command_reply_free(cmd);
1540 }
1541  
1542 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1543 {
1544   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1545   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1546   SilcCommandStatus status;
1547   unsigned char *tmp;
1548
1549   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1550   SILC_GET16_MSB(status, tmp);
1551   if (status != SILC_STATUS_OK) {
1552     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1553              "%s", silc_client_command_status_message(status));
1554     COMMAND_REPLY_ERROR;
1555     goto out;
1556   }
1557
1558   /* Notify application */
1559   COMMAND_REPLY((ARGS));
1560
1561  out:
1562   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1563   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1564   silc_client_command_reply_free(cmd);
1565 }
1566  
1567 /* Reply to LEAVE command. */
1568
1569 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1570 {
1571   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1572   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1573   SilcCommandStatus status;
1574   unsigned char *tmp;
1575
1576   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1577   SILC_GET16_MSB(status, tmp);
1578   if (status != SILC_STATUS_OK) {
1579     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1580              "%s", silc_client_command_status_message(status));
1581     COMMAND_REPLY_ERROR;
1582     goto out;
1583   }
1584
1585   /* Notify application */
1586   COMMAND_REPLY((ARGS));
1587
1588  out:
1589   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1590   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1591   silc_client_command_reply_free(cmd);
1592 }
1593
1594 /* Reply to USERS command. Received list of client ID's and theirs modes
1595    on the channel we requested. */
1596
1597 SILC_CLIENT_CMD_REPLY_FUNC(users)
1598 {
1599   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1600   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1601   SilcCommandStatus status;
1602   SilcIDCacheEntry id_cache = NULL;
1603   SilcChannelEntry channel;
1604   SilcChannelUser chu;
1605   SilcChannelID *channel_id = NULL;
1606   SilcBuffer client_id_list = NULL;
1607   SilcBuffer client_mode_list = NULL;
1608   unsigned char *tmp;
1609   uint32 tmp_len, list_count;
1610   int i;
1611   unsigned char **res_argv = NULL;
1612   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1613
1614   SILC_LOG_DEBUG(("Start"));
1615
1616   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1617   SILC_GET16_MSB(status, tmp);
1618   if (status != SILC_STATUS_OK) {
1619     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1620              "%s", silc_client_command_status_message(status));
1621     COMMAND_REPLY_ERROR;
1622     goto out;
1623   }
1624
1625   /* Get channel ID */
1626   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1627   if (!tmp) {
1628     COMMAND_REPLY_ERROR;
1629     goto out;
1630   }
1631   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1632   if (!channel_id) {
1633     COMMAND_REPLY_ERROR;
1634     goto out;
1635   }
1636   
1637   /* Get the list count */
1638   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1639   if (!tmp) {
1640     COMMAND_REPLY_ERROR;
1641     goto out;
1642   }
1643   SILC_GET32_MSB(list_count, tmp);
1644
1645   /* Get Client ID list */
1646   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1647   if (!tmp) {
1648     COMMAND_REPLY_ERROR;
1649     goto out;
1650   }
1651
1652   client_id_list = silc_buffer_alloc(tmp_len);
1653   silc_buffer_pull_tail(client_id_list, tmp_len);
1654   silc_buffer_put(client_id_list, tmp, tmp_len);
1655
1656   /* Get client mode list */
1657   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1658   if (!tmp) {
1659     COMMAND_REPLY_ERROR;
1660     goto out;
1661   }
1662
1663   client_mode_list = silc_buffer_alloc(tmp_len);
1664   silc_buffer_pull_tail(client_mode_list, tmp_len);
1665   silc_buffer_put(client_mode_list, tmp, tmp_len);
1666
1667   /* Get channel entry */
1668   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1669                                    &id_cache)) {
1670     /* Resolve the channel from server */
1671     silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
1672     
1673     /* Register pending command callback. After we've received the channel
1674        information we will reprocess this command reply by re-calling this
1675        USERS command reply callback. */
1676     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1677                                 NULL, silc_client_command_reply_users, cmd);
1678     return;
1679   } else {
1680     channel = (SilcChannelEntry)id_cache->context;
1681   }
1682
1683   /* Remove old client list from channel. */
1684   silc_list_start(channel->clients);
1685   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1686     silc_list_del(channel->clients, chu);
1687     silc_free(chu);
1688   }
1689
1690   /* Cache the received Client ID's and modes. */
1691   for (i = 0; i < list_count; i++) {
1692     uint16 idp_len;
1693     uint32 mode;
1694     SilcClientID *client_id;
1695     SilcClientEntry client;
1696
1697     /* Client ID */
1698     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1699     idp_len += 4;
1700     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1701     if (!client_id)
1702       continue;
1703
1704     /* Mode */
1705     SILC_GET32_MSB(mode, client_mode_list->data);
1706
1707     /* Check if we have this client cached already. */
1708     id_cache = NULL;
1709     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1710                                     (void *)client_id, 
1711                                     NULL, NULL, 
1712                                     silc_hash_client_id_compare, NULL,
1713                                     &id_cache);
1714
1715     if (!id_cache || !((SilcClientEntry)id_cache->context)->username ||
1716         !((SilcClientEntry)id_cache->context)->realname) {
1717
1718       if (id_cache && id_cache->context) {
1719         SilcClientEntry client_entry = (SilcClientEntry)id_cache->context;
1720         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
1721           client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
1722           silc_buffer_pull(client_id_list, idp_len);
1723           silc_buffer_pull(client_mode_list, 4);
1724           continue;
1725         }
1726         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
1727       }
1728
1729       /* No we don't have it (or it is incomplete in information), query
1730          it from the server. Assemble argument table that will be sent
1731          for the WHOIS command later. */
1732       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1733                               (res_argc + 1));
1734       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1735                                    (res_argc + 1));
1736       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1737                                     (res_argc + 1));
1738       res_argv[res_argc] = client_id_list->data;
1739       res_argv_lens[res_argc] = idp_len;
1740       res_argv_types[res_argc] = res_argc + 3;
1741       res_argc++;
1742     } else {
1743       /* Found the client, join it to the channel */
1744       client = (SilcClientEntry)id_cache->context;
1745       chu = silc_calloc(1, sizeof(*chu));
1746       chu->client = client;
1747       chu->mode = mode;
1748       silc_list_add(channel->clients, chu);
1749
1750       silc_free(client_id);
1751       id_cache = NULL;
1752     }
1753
1754     silc_buffer_pull(client_id_list, idp_len);
1755     silc_buffer_pull(client_mode_list, 4);
1756   }
1757
1758   /* Query the client information from server if the list included clients
1759      that we don't know about. */
1760   if (res_argc) {
1761     SilcBuffer res_cmd;
1762
1763     /* Send the WHOIS command to server */
1764     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1765                                           res_argc, res_argv, res_argv_lens,
1766                                           res_argv_types, ++conn->cmd_ident);
1767     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1768                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1769                             TRUE);
1770
1771     /* Register pending command callback. After we've received the WHOIS
1772        command reply we will reprocess this command reply by re-calling this
1773        USERS command reply callback. */
1774     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1775                                 NULL, silc_client_command_reply_users, cmd);
1776
1777     silc_buffer_free(res_cmd);
1778     if (channel_id)
1779       silc_free(channel_id);
1780
1781     silc_free(res_argv);
1782     silc_free(res_argv_lens);
1783     silc_free(res_argv_types);
1784     return;
1785   }
1786
1787   /* Notify application */
1788   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1789
1790  out:
1791   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1792   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1793   silc_client_command_reply_free(cmd);
1794   silc_free(channel_id);
1795   if (client_id_list)
1796     silc_buffer_free(client_id_list);
1797   if (client_mode_list)
1798     silc_buffer_free(client_mode_list);
1799 }
1800
1801 /* Received command reply to GETKEY command. WE've received the remote
1802    client's public key. */
1803
1804 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1805 {
1806   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1807   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1808   SilcCommandStatus status;
1809   SilcIDCacheEntry id_cache;
1810   SilcIDPayload idp = NULL;
1811   SilcClientID *client_id = NULL;
1812   SilcClientEntry client_entry;
1813   SilcServerID *server_id = NULL;
1814   SilcServerEntry server_entry;
1815   SilcSKEPKType type;
1816   unsigned char *tmp, *pk;
1817   uint32 len;
1818   uint16 pk_len;
1819   SilcIdType id_type;
1820   SilcPublicKey public_key = NULL;
1821
1822   SILC_LOG_DEBUG(("Start"));
1823
1824   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1825   SILC_GET16_MSB(status, tmp);
1826   if (status != SILC_STATUS_OK) {
1827     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1828                           "%s", silc_client_command_status_message(status));
1829     COMMAND_REPLY_ERROR;
1830     goto out;
1831   }
1832
1833   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1834   if (!tmp) {
1835     COMMAND_REPLY_ERROR;
1836     goto out;
1837   }
1838   idp = silc_id_payload_parse_data(tmp, len);
1839   if (!idp) {
1840     COMMAND_REPLY_ERROR;
1841     goto out;
1842   }
1843
1844   /* Get the public key payload */
1845   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1846   if (tmp) {
1847     /* Decode the public key */
1848     SILC_GET16_MSB(pk_len, tmp);
1849     SILC_GET16_MSB(type, tmp + 2);
1850     pk = tmp + 4;
1851     
1852     if (type != SILC_SKE_PK_TYPE_SILC) {
1853       COMMAND_REPLY_ERROR;
1854       goto out;
1855     }
1856     
1857     if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) {
1858       COMMAND_REPLY_ERROR;
1859       goto out;
1860     }
1861   } 
1862    
1863   id_type = silc_id_payload_get_type(idp);
1864   if (id_type == SILC_ID_CLIENT) {
1865     /* Received client's public key */
1866     client_id = silc_id_payload_get_id(idp);
1867     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1868                                          (void *)client_id, 
1869                                          NULL, NULL, 
1870                                          silc_hash_client_id_compare, NULL,
1871                                          &id_cache)) {
1872       COMMAND_REPLY_ERROR;
1873       goto out;
1874     }
1875
1876     client_entry = (SilcClientEntry)id_cache->context;
1877
1878     /* Notify application */
1879     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1880   } else if (id_type == SILC_ID_SERVER) {
1881     /* Received server's public key */
1882     server_id = silc_id_payload_get_id(idp);
1883     if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
1884                                      &id_cache)) {
1885       COMMAND_REPLY_ERROR;
1886       goto out;
1887     }
1888
1889     server_entry = (SilcServerEntry)id_cache->context;
1890
1891     /* Notify application */
1892     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1893   }
1894
1895  out:
1896   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1897   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1898   if (idp)
1899     silc_id_payload_free(idp);
1900   if (public_key)
1901     silc_pkcs_public_key_free(public_key);
1902   silc_free(client_id);
1903   silc_free(server_id);
1904   silc_client_command_reply_free(cmd);
1905 }