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