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