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