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