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