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