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