575ba3483b8873a43773fbecd4e279ad120e8fde
[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 *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   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1277   if (!tmp) {
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, tmp);
1329   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1330     if (chu->client == client_entry) {
1331       chu->mode = mode;
1332       break;
1333     }
1334   }
1335
1336   /* Notify application */
1337   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1338   silc_free(client_id);
1339   silc_free(channel_id);
1340   
1341  out:
1342   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1343   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1344   silc_client_command_reply_free(cmd);
1345 }
1346
1347 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1348 {
1349   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1350   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1351   SilcCommandStatus status;
1352   unsigned char *tmp;
1353
1354   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1355   SILC_GET16_MSB(status, tmp);
1356   if (status != SILC_STATUS_OK) {
1357     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1358              "%s", silc_client_command_status_message(status));
1359     COMMAND_REPLY_ERROR;
1360     goto out;
1361   }
1362
1363   /* Notify application */
1364   COMMAND_REPLY((ARGS));
1365
1366  out:
1367   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1368   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1369   silc_client_command_reply_free(cmd);
1370 }
1371
1372 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1373 {
1374   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1375   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1376   SilcCommandStatus status;
1377   unsigned char *tmp;
1378
1379   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1380   SILC_GET16_MSB(status, tmp);
1381   if (status != SILC_STATUS_OK) {
1382     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1383              "%s", silc_client_command_status_message(status));
1384     COMMAND_REPLY_ERROR;
1385     goto out;
1386   }
1387
1388   /* Notify application */
1389   COMMAND_REPLY((ARGS));
1390
1391  out:
1392   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1393   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1394   silc_client_command_reply_free(cmd);
1395 }
1396
1397 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1398 {
1399   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1400   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1401   SilcCommandStatus status;
1402   unsigned char *tmp;
1403
1404   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1405   SILC_GET16_MSB(status, tmp);
1406   if (status != SILC_STATUS_OK) {
1407     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1408              "%s", silc_client_command_status_message(status));
1409     COMMAND_REPLY_ERROR;
1410     goto out;
1411   }
1412
1413   /* Notify application */
1414   COMMAND_REPLY((ARGS));
1415
1416  out:
1417   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1418   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1419   silc_client_command_reply_free(cmd);
1420 }
1421
1422 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1423 {
1424   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1425   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1426   SilcCommandStatus status;
1427   unsigned char *tmp;
1428
1429   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1430   SILC_GET16_MSB(status, tmp);
1431   if (status != SILC_STATUS_OK) {
1432     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1433              "%s", silc_client_command_status_message(status));
1434     COMMAND_REPLY_ERROR;
1435     goto out;
1436   }
1437
1438   /* Notify application */
1439   COMMAND_REPLY((ARGS));
1440
1441  out:
1442   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1443   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1444   silc_client_command_reply_free(cmd);
1445 }
1446
1447 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1448 {
1449   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1450   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1451   SilcCommandStatus status;
1452   SilcIDCacheEntry id_cache = NULL;
1453   SilcChannelEntry channel;
1454   SilcChannelID *channel_id;
1455   unsigned char *tmp;
1456   uint32 len;
1457
1458   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1459   SILC_GET16_MSB(status, tmp);
1460   if (status != SILC_STATUS_OK) {
1461     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1462              "%s", silc_client_command_status_message(status));
1463     COMMAND_REPLY_ERROR;
1464     goto out;
1465   }
1466
1467   /* Take Channel ID */
1468   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1469   if (!tmp)
1470     goto out;
1471
1472   channel_id = silc_id_payload_parse_id(tmp, len);
1473   if (!channel_id)
1474     goto out;
1475
1476   /* Get the channel entry */
1477   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1478                                    &id_cache)) {
1479     silc_free(channel_id);
1480     COMMAND_REPLY_ERROR;
1481     goto out;
1482   }
1483   
1484   channel = (SilcChannelEntry)id_cache->context;
1485
1486   /* Get the ban list */
1487   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1488
1489   /* Notify application */
1490   COMMAND_REPLY((ARGS, channel, tmp));
1491
1492  out:
1493   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1494   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1495   silc_client_command_reply_free(cmd);
1496 }
1497
1498 SILC_CLIENT_CMD_REPLY_FUNC(close)
1499 {
1500   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1501   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1502   SilcCommandStatus status;
1503   unsigned char *tmp;
1504
1505   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1506   SILC_GET16_MSB(status, tmp);
1507   if (status != SILC_STATUS_OK) {
1508     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1509              "%s", silc_client_command_status_message(status));
1510     COMMAND_REPLY_ERROR;
1511     goto out;
1512   }
1513
1514   /* Notify application */
1515   COMMAND_REPLY((ARGS));
1516
1517  out:
1518   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1519   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1520   silc_client_command_reply_free(cmd);
1521 }
1522  
1523 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1524 {
1525   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1526   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1527   SilcCommandStatus status;
1528   unsigned char *tmp;
1529
1530   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1531   SILC_GET16_MSB(status, tmp);
1532   if (status != SILC_STATUS_OK) {
1533     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1534              "%s", silc_client_command_status_message(status));
1535     COMMAND_REPLY_ERROR;
1536     goto out;
1537   }
1538
1539   /* Notify application */
1540   COMMAND_REPLY((ARGS));
1541
1542  out:
1543   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1544   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1545   silc_client_command_reply_free(cmd);
1546 }
1547  
1548 /* Reply to LEAVE command. */
1549
1550 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1551 {
1552   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1553   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1554   SilcCommandStatus status;
1555   unsigned char *tmp;
1556
1557   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1558   SILC_GET16_MSB(status, tmp);
1559   if (status != SILC_STATUS_OK) {
1560     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1561              "%s", silc_client_command_status_message(status));
1562     COMMAND_REPLY_ERROR;
1563     goto out;
1564   }
1565
1566   /* Notify application */
1567   COMMAND_REPLY((ARGS));
1568
1569  out:
1570   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1571   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1572   silc_client_command_reply_free(cmd);
1573 }
1574
1575 /* Reply to USERS command. Received list of client ID's and theirs modes
1576    on the channel we requested. */
1577
1578 SILC_CLIENT_CMD_REPLY_FUNC(users)
1579 {
1580   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1581   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1582   SilcCommandStatus status;
1583   SilcIDCacheEntry id_cache = NULL;
1584   SilcChannelEntry channel;
1585   SilcChannelUser chu;
1586   SilcChannelID *channel_id = NULL;
1587   SilcBuffer client_id_list = NULL;
1588   SilcBuffer client_mode_list = NULL;
1589   unsigned char *tmp;
1590   uint32 tmp_len, list_count;
1591   int i;
1592   unsigned char **res_argv = NULL;
1593   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1594
1595   SILC_LOG_DEBUG(("Start"));
1596
1597   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1598   SILC_GET16_MSB(status, tmp);
1599   if (status != SILC_STATUS_OK) {
1600     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1601              "%s", silc_client_command_status_message(status));
1602     COMMAND_REPLY_ERROR;
1603     goto out;
1604   }
1605
1606   /* Get channel ID */
1607   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1608   if (!tmp) {
1609     COMMAND_REPLY_ERROR;
1610     goto out;
1611   }
1612   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1613   if (!channel_id) {
1614     COMMAND_REPLY_ERROR;
1615     goto out;
1616   }
1617   
1618   /* Get the list count */
1619   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1620   if (!tmp) {
1621     COMMAND_REPLY_ERROR;
1622     goto out;
1623   }
1624   SILC_GET32_MSB(list_count, tmp);
1625
1626   /* Get Client ID list */
1627   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1628   if (!tmp) {
1629     COMMAND_REPLY_ERROR;
1630     goto out;
1631   }
1632
1633   client_id_list = silc_buffer_alloc(tmp_len);
1634   silc_buffer_pull_tail(client_id_list, tmp_len);
1635   silc_buffer_put(client_id_list, tmp, tmp_len);
1636
1637   /* Get client mode list */
1638   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1639   if (!tmp) {
1640     COMMAND_REPLY_ERROR;
1641     goto out;
1642   }
1643
1644   client_mode_list = silc_buffer_alloc(tmp_len);
1645   silc_buffer_pull_tail(client_mode_list, tmp_len);
1646   silc_buffer_put(client_mode_list, tmp, tmp_len);
1647
1648   /* Get channel entry */
1649   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1650                                    &id_cache)) {
1651     /* Resolve the channel from server */
1652     silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
1653     
1654     /* Register pending command callback. After we've received the channel
1655        information we will reprocess this command reply by re-calling this
1656        USERS command reply callback. */
1657     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1658                                 NULL, silc_client_command_reply_users, cmd);
1659     return;
1660   } else {
1661     channel = (SilcChannelEntry)id_cache->context;
1662   }
1663
1664   /* Remove old client list from channel. */
1665   silc_list_start(channel->clients);
1666   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1667     silc_list_del(channel->clients, chu);
1668     silc_free(chu);
1669   }
1670
1671   /* Cache the received Client ID's and modes. */
1672   for (i = 0; i < list_count; i++) {
1673     uint16 idp_len;
1674     uint32 mode;
1675     SilcClientID *client_id;
1676     SilcClientEntry client;
1677
1678     /* Client ID */
1679     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1680     idp_len += 4;
1681     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1682     if (!client_id)
1683       continue;
1684
1685     /* Mode */
1686     SILC_GET32_MSB(mode, client_mode_list->data);
1687
1688     /* Check if we have this client cached already. */
1689     id_cache = NULL;
1690     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1691                                     (void *)client_id, 
1692                                     NULL, NULL, 
1693                                     silc_hash_client_id_compare, NULL,
1694                                     &id_cache);
1695
1696     if (!id_cache || !((SilcClientEntry)id_cache->context)->username ||
1697         !((SilcClientEntry)id_cache->context)->realname) {
1698       /* No we don't have it (or it is incomplete in information), query
1699          it from the server. Assemble argument table that will be sent
1700          for the WHOIS command later. */
1701       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1702                               (res_argc + 1));
1703       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1704                                    (res_argc + 1));
1705       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1706                                     (res_argc + 1));
1707       res_argv[res_argc] = client_id_list->data;
1708       res_argv_lens[res_argc] = idp_len;
1709       res_argv_types[res_argc] = res_argc + 3;
1710       res_argc++;
1711     } else {
1712       /* Found the client, join it to the channel */
1713       client = (SilcClientEntry)id_cache->context;
1714       chu = silc_calloc(1, sizeof(*chu));
1715       chu->client = client;
1716       chu->mode = mode;
1717       silc_list_add(channel->clients, chu);
1718
1719       silc_free(client_id);
1720       id_cache = NULL;
1721     }
1722
1723     silc_buffer_pull(client_id_list, idp_len);
1724     silc_buffer_pull(client_mode_list, 4);
1725   }
1726
1727   /* Query the client information from server if the list included clients
1728      that we don't know about. */
1729   if (res_argc) {
1730     SilcBuffer res_cmd;
1731
1732     /* Send the WHOIS command to server */
1733     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1734                                           res_argc, res_argv, res_argv_lens,
1735                                           res_argv_types, ++conn->cmd_ident);
1736     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1737                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1738                             TRUE);
1739
1740     /* Register pending command callback. After we've received the WHOIS
1741        command reply we will reprocess this command reply by re-calling this
1742        USERS command reply callback. */
1743     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1744                                 NULL, silc_client_command_reply_users, cmd);
1745
1746     silc_buffer_free(res_cmd);
1747     if (channel_id)
1748       silc_free(channel_id);
1749
1750     silc_free(res_argv);
1751     silc_free(res_argv_lens);
1752     silc_free(res_argv_types);
1753     return;
1754   }
1755
1756   /* Notify application */
1757   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1758
1759  out:
1760   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1761   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1762   silc_client_command_reply_free(cmd);
1763   silc_free(channel_id);
1764   if (client_id_list)
1765     silc_buffer_free(client_id_list);
1766   if (client_mode_list)
1767     silc_buffer_free(client_mode_list);
1768 }
1769
1770 /* Received command reply to GETKEY command. WE've received the remote
1771    client's public key. */
1772
1773 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1774 {
1775   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1776   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1777   SilcCommandStatus status;
1778   SilcIDCacheEntry id_cache;
1779   SilcIDPayload idp = NULL;
1780   SilcClientID *client_id = NULL;
1781   SilcClientEntry client_entry;
1782   SilcServerID *server_id = NULL;
1783   SilcServerEntry server_entry;
1784   SilcSKEPKType type;
1785   unsigned char *tmp, *pk;
1786   uint32 len;
1787   uint16 pk_len;
1788   SilcIdType id_type;
1789   SilcPublicKey public_key = NULL;
1790
1791   SILC_LOG_DEBUG(("Start"));
1792
1793   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1794   SILC_GET16_MSB(status, tmp);
1795   if (status != SILC_STATUS_OK) {
1796     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1797                           "%s", silc_client_command_status_message(status));
1798     COMMAND_REPLY_ERROR;
1799     goto out;
1800   }
1801
1802   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1803   if (!tmp) {
1804     COMMAND_REPLY_ERROR;
1805     goto out;
1806   }
1807   idp = silc_id_payload_parse_data(tmp, len);
1808   if (!idp) {
1809     COMMAND_REPLY_ERROR;
1810     goto out;
1811   }
1812
1813   /* Get the public key payload */
1814   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1815   if (tmp) {
1816     /* Decode the public key */
1817     SILC_GET16_MSB(pk_len, tmp);
1818     SILC_GET16_MSB(type, tmp + 2);
1819     pk = tmp + 4;
1820     
1821     if (type != SILC_SKE_PK_TYPE_SILC) {
1822       COMMAND_REPLY_ERROR;
1823       goto out;
1824     }
1825     
1826     if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) {
1827       COMMAND_REPLY_ERROR;
1828       goto out;
1829     }
1830   } 
1831    
1832   id_type = silc_id_payload_get_type(idp);
1833   if (id_type == SILC_ID_CLIENT) {
1834     /* Received client's public key */
1835     client_id = silc_id_payload_get_id(idp);
1836     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1837                                          (void *)client_id, 
1838                                          NULL, NULL, 
1839                                          silc_hash_client_id_compare, NULL,
1840                                          &id_cache)) {
1841       COMMAND_REPLY_ERROR;
1842       goto out;
1843     }
1844
1845     client_entry = (SilcClientEntry)id_cache->context;
1846
1847     /* Notify application */
1848     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1849   } else if (id_type == SILC_ID_SERVER) {
1850     /* Received server's public key */
1851     server_id = silc_id_payload_get_id(idp);
1852     if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
1853                                      &id_cache)) {
1854       COMMAND_REPLY_ERROR;
1855       goto out;
1856     }
1857
1858     server_entry = (SilcServerEntry)id_cache->context;
1859
1860     /* Notify application */
1861     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1862   }
1863
1864  out:
1865   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1866   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1867   if (idp)
1868     silc_id_payload_free(idp);
1869   if (public_key)
1870     silc_pkcs_public_key_free(public_key);
1871   silc_free(client_id);
1872   silc_free(server_id);
1873   silc_client_command_reply_free(cmd);
1874 }