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