updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
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
35 #include "clientlibincludes.h"
36 #include "client_internal.h"
37
38 const SilcCommandStatusMessage silc_command_status_messages[] = {
39
40   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
41   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
42   { STAT(NO_SUCH_SERVER),    "No such server" },
43   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
44   { STAT(NO_RECIPIENT),      "No recipient given" },
45   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
46   { STAT(WILDCARDS),         "Unknown command" },
47   { STAT(NO_CLIENT_ID),      "No Client ID given" },
48   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
49   { STAT(NO_SERVER_ID),      "No Server ID given" },
50   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
51   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
52   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
53   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
54   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
55   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
56   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
57   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
58   { STAT(NOT_REGISTERED),    "You have not registered" },
59   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
60   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
61   { STAT(PERM_DENIED),       "Permission denied" },
62   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
63   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
64   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
65   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
66   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
67   { STAT(UNKNOWN_MODE),    "Unknown mode" },
68   { STAT(NOT_YOU),         "Cannot change mode for other users" },
69   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
70   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
71   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
72   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
73   { STAT(BAD_NICKNAME),    "Bad nickname" },
74   { STAT(BAD_CHANNEL),     "Bad channel name" },
75   { STAT(AUTH_FAILED),     "Authentication failed" },
76   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
77   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
78
79   { 0, NULL }
80 };
81 /* Command reply operation that is called at the end of all command replys. 
82    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
83 #define COMMAND_REPLY(args) cmd->client->internal->ops->command_reply args
84 #define ARGS cmd->client, cmd->sock->user_data,                         \
85              cmd->payload, TRUE, silc_command_get(cmd->payload), status
86
87 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
88 #define COMMAND_REPLY_ERROR cmd->client->internal->ops->                \
89   command_reply(cmd->client, cmd->sock->user_data, cmd->payload,        \
90   FALSE, silc_command_get(cmd->payload), status)
91
92 /* All functions that call the COMMAND_CHECK_STATUS or the
93    COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
94
95 #define COMMAND_CHECK_STATUS                                              \
96 do {                                                                      \
97   SILC_LOG_DEBUG(("Start"));                                              \
98   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
99   if (status != SILC_STATUS_OK)  {                                        \
100     COMMAND_REPLY_ERROR;                                                  \
101     goto out;                                                             \
102   }                                                                       \
103 } while(0)
104
105 #define COMMAND_CHECK_STATUS_LIST                                         \
106 do {                                                                      \
107   SILC_LOG_DEBUG(("Start"));                                              \
108   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
109   if (status != SILC_STATUS_OK &&                                         \
110       status != SILC_STATUS_LIST_START &&                                 \
111       status != SILC_STATUS_LIST_ITEM &&                                  \
112       status != SILC_STATUS_LIST_END) {                                   \
113     COMMAND_REPLY_ERROR;                                                  \
114     goto out;                                                             \
115   }                                                                       \
116 } while(0)
117
118 /* Process received command reply. */
119
120 void silc_client_command_reply_process(SilcClient client,
121                                        SilcSocketConnection sock,
122                                        SilcPacketContext *packet)
123 {
124   SilcBuffer buffer = packet->buffer;
125   SilcClientCommand cmd;
126   SilcClientCommandReplyContext ctx;
127   SilcCommandPayload payload;
128   SilcCommand command;
129   SilcCommandCb reply = NULL;
130   
131   /* Get command reply payload from packet */
132   payload = silc_command_payload_parse(buffer->data, buffer->len);
133   if (!payload) {
134     /* Silently ignore bad reply packet */
135     SILC_LOG_DEBUG(("Bad command reply packet"));
136     return;
137   }
138   
139   /* Allocate command reply context. This must be free'd by the
140      command reply routine receiving it. */
141   ctx = silc_calloc(1, sizeof(*ctx));
142   ctx->client = client;
143   ctx->sock = sock;
144   ctx->payload = payload;
145   ctx->args = silc_command_get_args(ctx->payload);
146   ctx->packet = packet;
147   ctx->ident = silc_command_get_ident(ctx->payload);
148
149   /* Check for pending commands and mark to be exeucted */
150   silc_client_command_pending_check(sock->user_data, ctx, 
151                                     silc_command_get(ctx->payload), 
152                                     ctx->ident);
153
154   /* Execute command reply */
155
156   command = silc_command_get(ctx->payload);
157
158   /* Try to find matching the command identifier */
159   silc_list_start(client->internal->commands);
160   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
161     if (cmd->cmd == command && !cmd->ident)
162       reply = cmd->reply;
163     if (cmd->cmd == command && cmd->ident == ctx->ident) {
164       (*cmd->reply)((void *)ctx, NULL);
165       break;
166     }
167   }
168
169   if (cmd == SILC_LIST_END) {
170     if (reply)
171       /* No specific identifier for command reply, call first one found */
172       (*reply)(ctx, NULL);
173     else
174       silc_free(ctx);
175   }
176 }
177
178 /* Returns status message string */
179
180 char *silc_client_command_status_message(SilcCommandStatus status)
181 {
182   int i;
183
184   for (i = 0; silc_command_status_messages[i].message; i++) {
185     if (silc_command_status_messages[i].status == status)
186       break;
187   }
188
189   if (silc_command_status_messages[i].message == NULL)
190     return NULL;
191
192   return silc_command_status_messages[i].message;
193 }
194
195 /* Free command reply context and its internals. */
196
197 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
198 {
199   if (cmd) {
200     silc_command_payload_free(cmd->payload);
201     silc_free(cmd);
202   }
203 }
204
205 static void 
206 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
207                                      SilcCommandStatus status,
208                                      bool notify)
209 {
210   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
211   SilcClientID *client_id;
212   SilcIDCacheEntry id_cache = NULL;
213   SilcClientEntry client_entry = NULL;
214   int argc;
215   uint32 len;
216   unsigned char *id_data, *tmp;
217   char *nickname = NULL, *username = NULL;
218   char *realname = NULL;
219   uint32 idle = 0, mode = 0;
220   SilcBuffer channels = NULL;
221   unsigned char *fingerprint;
222   uint32 fingerprint_len;
223   
224   argc = silc_argument_get_arg_num(cmd->args);
225
226   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
227   if (!id_data) {
228     if (notify)
229       COMMAND_REPLY_ERROR;
230     return;
231   }
232   
233   client_id = silc_id_payload_parse_id(id_data, len);
234   if (!client_id) {
235     if (notify)
236       COMMAND_REPLY_ERROR;
237     return;
238   }
239   
240   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
241   username = silc_argument_get_arg_type(cmd->args, 4, &len);
242   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
243   if (!nickname || !username || !realname) {
244     if (notify)
245       COMMAND_REPLY_ERROR;
246     return;
247   }
248
249   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
250   if (tmp) {
251     channels = silc_buffer_alloc(len);
252     silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels));
253     silc_buffer_put(channels, tmp, len);
254   }
255
256   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
257   if (tmp)
258     SILC_GET32_MSB(mode, tmp);
259
260   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
261   if (tmp)
262     SILC_GET32_MSB(idle, tmp);
263
264   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
265
266   /* Check if we have this client cached already. */
267   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
268                                        NULL, NULL, 
269                                        silc_hash_client_id_compare, NULL,
270                                        &id_cache)) {
271     SILC_LOG_DEBUG(("Adding new client entry"));
272     client_entry = 
273       silc_client_add_client(cmd->client, conn, nickname, username, realname,
274                              client_id, mode);
275   } else {
276     client_entry = (SilcClientEntry)id_cache->context;
277     silc_client_update_client(cmd->client, conn, client_entry, 
278                               nickname, username, realname, mode);
279     silc_free(client_id);
280   }
281
282   if (fingerprint && !client_entry->fingerprint) {
283     client_entry->fingerprint = 
284       silc_calloc(fingerprint_len, 
285                   sizeof(*client_entry->fingerprint));
286     memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
287     client_entry->fingerprint_len = fingerprint_len;
288   }
289
290   if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
291     client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
292
293   /* Notify application */
294   if (!cmd->callback && notify)
295     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
296                    channels, mode, idle, fingerprint));
297
298   if (channels)
299     silc_buffer_free(channels);
300 }
301
302 /* Received reply for WHOIS command. This maybe called several times
303    for one WHOIS command as server may reply with list of results. */
304
305 SILC_CLIENT_CMD_REPLY_FUNC(whois)
306 {
307   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
308   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
309   SilcCommandStatus status;
310
311   COMMAND_CHECK_STATUS_LIST;
312
313   /* Save WHOIS info */
314   silc_client_command_reply_whois_save(cmd, status, TRUE);
315
316   /* Pending callbacks are not executed if this was an list entry */
317   if (status != SILC_STATUS_OK &&
318       status != SILC_STATUS_LIST_END) {
319     silc_client_command_reply_free(cmd);
320     return;
321   }
322
323  out:
324   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
325   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
326
327   /* If we received notify for invalid ID we'll remove the ID if we
328      have it cached. */
329   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
330     SilcClientEntry client_entry;
331     uint32 tmp_len;
332     unsigned char *tmp =
333       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
334                                  2, &tmp_len);
335     if (tmp) {
336       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
337       if (client_id) {
338         client_entry = silc_client_get_client_by_id(cmd->client, conn,
339                                                     client_id);
340         if (client_entry)
341           silc_client_del_client(cmd->client, conn, client_entry);
342         silc_free(client_id);
343       }
344     }
345   }
346
347   silc_client_command_reply_free(cmd);
348 }
349
350 /* Received reply for WHOWAS command. */
351
352 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
353 {
354   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
355   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
356   SilcCommandStatus status;
357   SilcClientID *client_id;
358   SilcIDCacheEntry id_cache = NULL;
359   SilcClientEntry client_entry = NULL;
360   uint32 len;
361   unsigned char *id_data;
362   char *nickname, *username;
363   char *realname = NULL;
364
365   COMMAND_CHECK_STATUS_LIST;
366
367   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
368   if (!id_data) {
369     COMMAND_REPLY_ERROR;
370     goto out;
371   }
372   
373   client_id = silc_id_payload_parse_id(id_data, len);
374   if (!client_id) {
375     COMMAND_REPLY_ERROR;
376     goto out;
377   }
378
379   /* Get the client entry, if exists */
380   if (silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
381                                       NULL, NULL, 
382                                       silc_hash_client_id_compare, NULL,
383                                       &id_cache)) 
384     client_entry = (SilcClientEntry)id_cache->context;
385   silc_free(client_id);
386
387   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
388   username = silc_argument_get_arg_type(cmd->args, 4, &len);
389   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
390   if (!nickname || !username) {
391     COMMAND_REPLY_ERROR;
392     goto out;
393   }
394
395   /* Notify application. We don't save any history information to any
396      cache. Just pass the data to the application for displaying on 
397      the screen. */
398   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
399
400   /* Pending callbacks are not executed if this was an list entry */
401   if (status != SILC_STATUS_OK &&
402       status != SILC_STATUS_LIST_END) {
403     silc_client_command_reply_free(cmd);
404     return;
405   }
406
407  out:
408   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
409   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
410   silc_client_command_reply_free(cmd);
411 }
412
413 static void 
414 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
415                                         SilcCommandStatus status,
416                                         bool notify)
417 {
418   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
419   SilcClient client = cmd->client;
420   SilcClientID *client_id = NULL;
421   SilcServerID *server_id = NULL;
422   SilcChannelID *channel_id = NULL;
423   SilcIDCacheEntry id_cache = NULL;
424   SilcClientEntry client_entry;
425   SilcServerEntry server_entry;
426   SilcChannelEntry channel_entry;
427   int argc;
428   uint32 len;
429   unsigned char *id_data;
430   char *name = NULL, *info = NULL;
431   SilcIDPayload idp = NULL;
432   SilcIdType id_type;
433   
434   argc = silc_argument_get_arg_num(cmd->args);
435
436   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
437   if (!id_data) {
438     if (notify)
439       COMMAND_REPLY_ERROR;
440     return;
441   }
442   idp = silc_id_payload_parse(id_data, len);
443   if (!idp) {
444     if (notify)
445       COMMAND_REPLY_ERROR;
446     return;
447   }
448
449   name = silc_argument_get_arg_type(cmd->args, 3, &len);
450   info = silc_argument_get_arg_type(cmd->args, 4, &len);
451
452   id_type = silc_id_payload_get_type(idp);
453
454   switch (id_type) {
455   case SILC_ID_CLIENT:
456     client_id = silc_id_payload_get_id(idp);
457
458     SILC_LOG_DEBUG(("Received client information"));
459
460     /* Check if we have this client cached already. */
461     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
462                                          (void *)client_id, 
463                                          NULL, NULL, 
464                                          silc_hash_client_id_compare, NULL,
465                                          &id_cache)) {
466       SILC_LOG_DEBUG(("Adding new client entry"));
467       client_entry = 
468         silc_client_add_client(cmd->client, conn, name, info, NULL,
469                                silc_id_dup(client_id, id_type), 0);
470     } else {
471       client_entry = (SilcClientEntry)id_cache->context;
472       silc_client_update_client(cmd->client, conn, client_entry, 
473                                 name, info, NULL, 0);
474     }
475
476     if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
477       client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
478
479     /* Notify application */
480     if (notify)
481       COMMAND_REPLY((ARGS, client_entry, name, info));
482     break;
483
484   case SILC_ID_SERVER:
485     server_id = silc_id_payload_get_id(idp);
486
487     SILC_LOG_DEBUG(("Received server information"));
488
489     /* Check if we have this server cached already. */
490     if (!silc_idcache_find_by_id_one(conn->server_cache, 
491                                      (void *)server_id, &id_cache)) {
492       SILC_LOG_DEBUG(("Adding new server entry"));
493       
494       server_entry = silc_calloc(1, sizeof(*server_entry));
495       server_entry->server_id = silc_id_dup(server_id, id_type);
496       if (name)
497         server_entry->server_name = strdup(name);
498       if (info)
499         server_entry->server_info = strdup(info);
500       
501       /* Add server to cache */
502       silc_idcache_add(conn->server_cache, server_entry->server_name,
503                        server_entry->server_id, (void *)server_entry, 
504                        0, NULL);
505     } else {
506       server_entry = (SilcServerEntry)id_cache->context;
507     }
508
509     /* Notify application */
510     if (notify)
511       COMMAND_REPLY((ARGS, server_entry, name, info));
512     break;
513
514   case SILC_ID_CHANNEL:
515     channel_id = silc_id_payload_get_id(idp);
516
517     SILC_LOG_DEBUG(("Received channel information"));
518
519     /* Check if we have this channel cached already. */
520     if (!silc_idcache_find_by_id_one(conn->channel_cache, 
521                                      (void *)channel_id, &id_cache)) {
522       if (!name)
523         break;
524
525       SILC_LOG_DEBUG(("Adding new channel entry"));
526       channel_entry = silc_client_new_channel_id(client, conn->sock, 
527                                                  strdup(name), 0, idp);
528     } else {
529       channel_entry = (SilcChannelEntry)id_cache->context;
530     }
531
532     /* Notify application */
533     if (notify)
534       COMMAND_REPLY((ARGS, channel_entry, name, info));
535     break;
536   }
537
538   silc_id_payload_free(idp);
539   silc_free(client_id);
540   silc_free(server_id);
541   silc_free(channel_id);
542 }
543
544 /* Received reply for IDENTIFY command. This maybe called several times
545    for one IDENTIFY command as server may reply with list of results. 
546    This is totally silent and does not print anything on screen. */
547
548 SILC_CLIENT_CMD_REPLY_FUNC(identify)
549 {
550   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
551   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
552   SilcCommandStatus status;
553
554   COMMAND_CHECK_STATUS_LIST;
555
556   /* Save IDENTIFY info */
557   silc_client_command_reply_identify_save(cmd, status, TRUE);
558
559   /* Pending callbacks are not executed if this was an list entry */
560   if (status != SILC_STATUS_OK &&
561       status != SILC_STATUS_LIST_END) {
562     silc_client_command_reply_free(cmd);
563     return;
564   }
565
566  out:
567   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
568   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
569
570   /* If we received notify for invalid ID we'll remove the ID if we
571      have it cached. */
572   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
573     SilcClientEntry client_entry;
574     uint32 tmp_len;
575     unsigned char *tmp =
576       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
577                                  2, &tmp_len);
578     if (tmp) {
579       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
580       if (client_id) {
581         client_entry = silc_client_get_client_by_id(cmd->client, conn,
582                                                     client_id);
583         if (client_entry)
584           silc_client_del_client(cmd->client, conn, client_entry);
585         silc_free(client_id);
586       }
587     }
588   }
589
590   silc_client_command_reply_free(cmd);
591 }
592
593 /* Received reply for command NICK. If everything went without errors
594    we just received our new Client ID. */
595
596 SILC_CLIENT_CMD_REPLY_FUNC(nick)
597 {
598   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
599   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
600   SilcCommandStatus status;
601   SilcIDPayload idp;
602   unsigned char *tmp;
603   uint32 argc, len;
604
605   SILC_LOG_DEBUG(("Start"));
606
607   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
608   if (status != SILC_STATUS_OK) {
609     cmd->client->internal->ops->say(
610                              cmd->client, 
611                              conn, SILC_CLIENT_MESSAGE_ERROR,
612                              "Cannot set nickname: %s", 
613                              silc_client_command_status_message(status));
614     COMMAND_REPLY_ERROR;
615     goto out;
616   }
617
618   argc = silc_argument_get_arg_num(cmd->args);
619   if (argc < 2 || argc > 2) {
620     cmd->client->internal->ops->say(
621                             cmd->client, 
622                             conn, SILC_CLIENT_MESSAGE_ERROR,
623                             "Cannot set nickname: bad reply to command");
624     COMMAND_REPLY_ERROR;
625     goto out;
626   }
627
628   /* Take received Client ID */
629   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
630   idp = silc_id_payload_parse(tmp, len);
631   if (!idp) {
632     COMMAND_REPLY_ERROR;
633     goto out;
634   }
635   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
636     
637   /* Notify application */
638   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
639   COMMAND_REPLY((ARGS, conn->local_entry));
640   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
641   silc_client_command_reply_free(cmd);
642   return;
643
644  out:
645   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
646   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
647   silc_client_command_reply_free(cmd);
648 }
649
650 /* Received reply to the LIST command. */
651
652 SILC_CLIENT_CMD_REPLY_FUNC(list)
653 {
654   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
655   SilcCommandStatus status;
656   unsigned char *tmp, *name, *topic;
657   uint32 usercount = 0;
658
659   COMMAND_CHECK_STATUS_LIST;
660
661   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
662   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
663   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
664   if (tmp)
665     SILC_GET32_MSB(usercount, tmp);
666
667   /* Notify application */
668   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
669
670   /* Pending callbacks are not executed if this was an list entry */
671   if (status != SILC_STATUS_OK &&
672       status != SILC_STATUS_LIST_END) {
673     silc_client_command_reply_free(cmd);
674     return;
675   }
676
677  out:
678   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
679   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
680   silc_client_command_reply_free(cmd);
681 }
682
683 /* Received reply to topic command. */
684
685 SILC_CLIENT_CMD_REPLY_FUNC(topic)
686 {
687   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
688   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
689   SilcCommandStatus status;
690   SilcChannelEntry channel;
691   SilcChannelID *channel_id = NULL;
692   SilcIDCacheEntry id_cache = NULL;
693   unsigned char *tmp;
694   char *topic;
695   uint32 argc, len;
696
697   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
698   if (status != SILC_STATUS_OK) {
699     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
700              "%s", silc_client_command_status_message(status));
701     COMMAND_REPLY_ERROR;
702     goto out;
703   }
704
705   argc = silc_argument_get_arg_num(cmd->args);
706   if (argc < 1 || argc > 3) {
707     COMMAND_REPLY_ERROR;
708     goto out;
709   }
710
711   /* Take Channel ID */
712   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
713   if (!tmp)
714     goto out;
715
716   /* Take topic */
717   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
718   if (!topic)
719     goto out;
720
721   channel_id = silc_id_payload_parse_id(tmp, len);
722   if (!channel_id)
723     goto out;
724
725   /* Get the channel entry */
726   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
727                                    &id_cache)) {
728     silc_free(channel_id);
729     COMMAND_REPLY_ERROR;
730     goto out;
731   }
732   
733   channel = (SilcChannelEntry)id_cache->context;
734
735   /* Notify application */
736   COMMAND_REPLY((ARGS, channel, topic));
737
738  out:
739   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
740   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
741   silc_client_command_reply_free(cmd);
742 }
743
744 /* Received reply to invite command. */
745
746 SILC_CLIENT_CMD_REPLY_FUNC(invite)
747 {
748   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
749   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
750   SilcCommandStatus status;
751   SilcChannelEntry channel;
752   SilcChannelID *channel_id;
753   SilcIDCacheEntry id_cache;
754   unsigned char *tmp;
755   uint32 len;
756
757   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
758   SILC_GET16_MSB(status, tmp);
759   if (status != SILC_STATUS_OK) {
760     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
761              "%s", silc_client_command_status_message(status));
762     COMMAND_REPLY_ERROR;
763     goto out;
764   }
765
766   /* Take Channel ID */
767   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
768   if (!tmp)
769     goto out;
770
771   channel_id = silc_id_payload_parse_id(tmp, len);
772   if (!channel_id)
773     goto out;
774
775   /* Get the channel entry */
776   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
777                                    &id_cache)) {
778     silc_free(channel_id);
779     COMMAND_REPLY_ERROR;
780     goto out;
781   }
782   
783   channel = (SilcChannelEntry)id_cache->context;
784
785   /* Get the invite list */
786   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
787
788   /* Notify application */
789   COMMAND_REPLY((ARGS, channel, tmp));
790
791  out:
792   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
793   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
794   silc_client_command_reply_free(cmd);
795 }
796
797 /* Received reply to the KILL command. */
798  
799 SILC_CLIENT_CMD_REPLY_FUNC(kill)
800 {
801   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
802   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
803   SilcCommandStatus status;
804
805   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
806   if (status != SILC_STATUS_OK) {
807     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
808              "%s", silc_client_command_status_message(status));
809     COMMAND_REPLY_ERROR;
810     goto out;
811   }
812
813   /* Notify application */
814   COMMAND_REPLY((ARGS));
815
816  out:
817   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
818   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
819   silc_client_command_reply_free(cmd);
820 }
821
822 /* Received reply to INFO command. We receive the server ID and some
823    information about the server user requested. */
824
825 SILC_CLIENT_CMD_REPLY_FUNC(info)
826 {
827   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
828   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
829   SilcCommandStatus status;
830   unsigned char *tmp;
831   SilcIDCacheEntry id_cache;
832   SilcServerEntry server;
833   SilcServerID *server_id = NULL;
834   char *server_name, *server_info;
835   uint32 len;
836
837   SILC_LOG_DEBUG(("Start"));
838
839   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
840   SILC_GET16_MSB(status, tmp);
841   if (status != SILC_STATUS_OK) {
842     cmd->client->internal->ops->say(
843                           cmd->client, conn, 
844                           SILC_CLIENT_MESSAGE_ERROR,
845                           "%s", 
846                           silc_client_command_status_message(status));
847     COMMAND_REPLY_ERROR;
848     goto out;
849   }
850
851   /* Get server ID */
852   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
853   if (!tmp)
854     goto out;
855
856   server_id = silc_id_payload_parse_id(tmp, len);
857   if (!server_id)
858     goto out;
859
860   /* Get server name */
861   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
862   if (!server_name)
863     goto out;
864
865   /* Get server info */
866   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
867   if (!server_info)
868     goto out;
869
870   /* See whether we have this server cached. If not create it. */
871   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
872                                    &id_cache)) {
873     SILC_LOG_DEBUG(("New server entry"));
874
875     server = silc_calloc(1, sizeof(*server));
876     server->server_name = strdup(server_name);
877     server->server_info = strdup(server_info);
878     server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
879
880     /* Add it to the cache */
881     silc_idcache_add(conn->server_cache, server->server_name,
882                      server->server_id, (void *)server, 0, NULL);
883   } else {
884     server = (SilcServerEntry)id_cache->context;
885   }
886   
887   /* Notify application */
888   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
889
890  out:
891   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
892   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
893   silc_free(server_id);
894   silc_client_command_reply_free(cmd);
895 }
896
897 /* Received reply to PING command. The reply time is shown to user. */
898
899 SILC_CLIENT_CMD_REPLY_FUNC(ping)
900 {
901   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
902   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
903   SilcCommandStatus status;
904   void *id;
905   int i;
906   time_t diff, curtime;
907
908   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
909   if (status != SILC_STATUS_OK) {
910     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
911              "%s", silc_client_command_status_message(status));
912     COMMAND_REPLY_ERROR;
913     goto out;
914   }
915
916   curtime = time(NULL);
917   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
918                       cmd->packet->src_id_type);
919   if (!id || !conn->ping) {
920     COMMAND_REPLY_ERROR;
921     goto out;
922   }
923
924   for (i = 0; i < conn->ping_count; i++) {
925     if (!conn->ping[i].dest_id)
926       continue;
927     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
928       diff = curtime - conn->ping[i].start_time;
929       cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
930                             "Ping reply from %s: %d second%s", 
931                             conn->ping[i].dest_name, diff, 
932                             diff == 1 ? "" : "s");
933       
934       conn->ping[i].start_time = 0;
935       silc_free(conn->ping[i].dest_id);
936       conn->ping[i].dest_id = NULL;
937       silc_free(conn->ping[i].dest_name);
938       conn->ping[i].dest_name = NULL;
939       break;
940     }
941   }
942
943   silc_free(id);
944
945   /* Notify application */
946   COMMAND_REPLY((ARGS));
947
948  out:
949   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
950   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
951   silc_client_command_reply_free(cmd);
952 }
953
954 /* Received reply for JOIN command. */
955
956 SILC_CLIENT_CMD_REPLY_FUNC(join)
957 {
958   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
959   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
960   SilcCommandStatus status;
961   SilcIDPayload idp = NULL;
962   SilcChannelEntry channel;
963   SilcIDCacheEntry id_cache = NULL;
964   SilcChannelUser chu;
965   uint32 argc, mode, len, list_count;
966   char *topic, *tmp, *channel_name = NULL, *hmac;
967   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
968   int i;
969
970   SILC_LOG_DEBUG(("Start"));
971
972   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
973   if (status != SILC_STATUS_OK) {
974     if (status != SILC_STATUS_ERR_USER_ON_CHANNEL)
975       cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
976                             "%s", silc_client_command_status_message(status));
977     COMMAND_REPLY_ERROR;
978     goto out;
979   }
980
981   argc = silc_argument_get_arg_num(cmd->args);
982   if (argc < 7 || argc > 14) {
983     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
984              "Cannot join channel: Bad reply packet");
985     COMMAND_REPLY_ERROR;
986     goto out;
987   }
988
989   /* Get channel name */
990   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
991   if (!tmp) {
992     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
993                           "Cannot join channel: Bad reply packet");
994     COMMAND_REPLY_ERROR;
995     goto out;
996   }
997   channel_name = strdup(tmp);
998
999   /* Get Channel ID */
1000   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1001   if (!tmp) {
1002     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1003                           "Cannot join channel: Bad reply packet");
1004     COMMAND_REPLY_ERROR;
1005     silc_free(channel_name);
1006     goto out;
1007   }
1008   idp = silc_id_payload_parse(tmp, len);
1009   if (!idp) {
1010     COMMAND_REPLY_ERROR;
1011     silc_free(channel_name);
1012     goto out;
1013   }
1014
1015   /* Get channel mode */
1016   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1017   if (tmp)
1018     SILC_GET32_MSB(mode, tmp);
1019   else
1020     mode = 0;
1021
1022   /* Get channel key */
1023   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
1024   if (tmp) {
1025     keyp = silc_buffer_alloc(len);
1026     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
1027     silc_buffer_put(keyp, tmp, len);
1028   }
1029
1030   /* Get topic */
1031   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1032
1033   /* If we have the channel entry, remove it and create a new one */
1034   channel = silc_client_get_channel(cmd->client, conn, channel_name);
1035   if (channel)
1036     silc_client_del_channel(cmd->client, conn, channel);
1037
1038   /* Save received Channel ID. This actually creates the channel */
1039   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
1040                                        mode, idp);
1041   silc_id_payload_free(idp);
1042
1043   conn->current_channel = channel;
1044
1045   /* Get hmac */
1046   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1047   if (hmac) {
1048     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1049       cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
1050                             "Cannot join channel: Unsupported HMAC `%s'",
1051                             hmac);
1052       COMMAND_REPLY_ERROR;
1053       silc_free(channel_name);
1054       goto out;
1055     }
1056   }
1057
1058   /* Get the list count */
1059   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1060   if (!tmp)
1061     goto out;
1062   SILC_GET32_MSB(list_count, tmp);
1063
1064   /* Get Client ID list */
1065   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1066   if (!tmp)
1067     goto out;
1068
1069   client_id_list = silc_buffer_alloc(len);
1070   silc_buffer_pull_tail(client_id_list, len);
1071   silc_buffer_put(client_id_list, tmp, len);
1072
1073   /* Get client mode list */
1074   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1075   if (!tmp)
1076     goto out;
1077
1078   client_mode_list = silc_buffer_alloc(len);
1079   silc_buffer_pull_tail(client_mode_list, len);
1080   silc_buffer_put(client_mode_list, tmp, len);
1081
1082   /* Add clients we received in the reply to the channel */
1083   for (i = 0; i < list_count; i++) {
1084     uint16 idp_len;
1085     uint32 mode;
1086     SilcClientID *client_id;
1087     SilcClientEntry client_entry;
1088
1089     /* Client ID */
1090     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1091     idp_len += 4;
1092     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1093     if (!client_id)
1094       continue;
1095
1096     /* Mode */
1097     SILC_GET32_MSB(mode, client_mode_list->data);
1098
1099     /* Check if we have this client cached already. */
1100     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1101                                          (void *)client_id, 
1102                                          NULL, NULL, 
1103                                          silc_hash_client_id_compare, NULL,
1104                                          &id_cache)) {
1105       /* No, we don't have it, add entry for it. */
1106       client_entry = 
1107         silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1108                                silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1109     } else {
1110       /* Yes, we have it already */
1111       client_entry = (SilcClientEntry)id_cache->context;
1112     }
1113
1114     /* Join the client to the channel */
1115     chu = silc_calloc(1, sizeof(*chu));
1116     chu->client = client_entry;
1117     chu->mode = mode;
1118     silc_list_add(channel->clients, chu);
1119     silc_free(client_id);
1120
1121     silc_buffer_pull(client_id_list, idp_len);
1122     silc_buffer_pull(client_mode_list, 4);
1123   }
1124   silc_buffer_push(client_id_list, client_id_list->data - 
1125                    client_id_list->head);
1126   silc_buffer_push(client_mode_list, client_mode_list->data - 
1127                    client_mode_list->head);
1128
1129   /* Save channel key */
1130   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1131     silc_client_save_channel_key(conn, keyp, channel);
1132
1133   /* Client is now joined to the channel */
1134   channel->on_channel = TRUE;
1135
1136   /* Notify application */
1137   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1138                  keyp ? keyp->head : NULL, NULL,
1139                  NULL, topic, hmac, list_count, client_id_list, 
1140                  client_mode_list));
1141
1142  out:
1143   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1144   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1145   silc_client_command_reply_free(cmd);
1146
1147   if (keyp)
1148     silc_buffer_free(keyp);
1149   if (client_id_list)
1150     silc_buffer_free(client_id_list);
1151   if (client_mode_list)
1152     silc_buffer_free(client_mode_list);
1153 }
1154
1155 /* Received reply for MOTD command */
1156
1157 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1158 {
1159   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1160   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1161   SilcCommandStatus status;
1162   uint32 argc, i;
1163   unsigned char *tmp;
1164   char *motd = NULL, *cp, line[256];
1165
1166   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1167   SILC_GET16_MSB(status, tmp);
1168   if (status != SILC_STATUS_OK) {
1169     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1170              "%s", silc_client_command_status_message(status));
1171     COMMAND_REPLY_ERROR;
1172     return;
1173   }
1174
1175   argc = silc_argument_get_arg_num(cmd->args);
1176   if (argc > 3) {
1177     COMMAND_REPLY_ERROR;
1178     goto out;
1179   }
1180
1181   if (argc == 3) {
1182     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1183     if (!motd) {
1184       COMMAND_REPLY_ERROR;
1185       goto out;
1186     }
1187
1188     i = 0;
1189     cp = motd;
1190     while(cp[i] != 0) {
1191       if (cp[i++] == '\n') {
1192         memset(line, 0, sizeof(line));
1193         strncat(line, cp, i - 1);
1194         cp += i;
1195         
1196         if (i == 2)
1197           line[0] = ' ';
1198         
1199         cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1200                               "%s", line);
1201         
1202         if (!strlen(cp))
1203           break;
1204         i = 0;
1205       }
1206     }
1207   }
1208
1209   /* Notify application */
1210   COMMAND_REPLY((ARGS, motd));
1211
1212  out:
1213   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1214   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1215   silc_client_command_reply_free(cmd);
1216 }
1217
1218 /* Received reply tot he UMODE command. Save the current user mode */
1219
1220 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1221 {
1222   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1223   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1224   SilcCommandStatus status;
1225   unsigned char *tmp;
1226   uint32 mode;
1227
1228   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1229   SILC_GET16_MSB(status, tmp);
1230   if (status != SILC_STATUS_OK) {
1231     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1232              "%s", silc_client_command_status_message(status));
1233     COMMAND_REPLY_ERROR;
1234     goto out;
1235   }
1236
1237   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1238   if (!tmp) {
1239     COMMAND_REPLY_ERROR;
1240     goto out;
1241   }
1242
1243   SILC_GET32_MSB(mode, tmp);
1244   conn->local_entry->mode = mode;
1245
1246   /* Notify application */
1247   COMMAND_REPLY((ARGS, mode));
1248
1249  out:
1250   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1251   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1252   silc_client_command_reply_free(cmd);
1253 }
1254
1255 /* Received reply for CMODE command. */
1256
1257 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1258 {
1259   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1260   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1261   SilcCommandStatus status;
1262   unsigned char *tmp;
1263   uint32 mode;
1264   SilcIDCacheEntry id_cache;
1265   SilcChannelID *channel_id;
1266   SilcChannelEntry channel;
1267   uint32 len;
1268
1269   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1270   if (status != SILC_STATUS_OK) {
1271     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1272              "%s", silc_client_command_status_message(status));
1273     COMMAND_REPLY_ERROR;
1274     goto out;
1275   }
1276
1277   /* Take Channel ID */
1278   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1279   if (!tmp)
1280     goto out;
1281   channel_id = silc_id_payload_parse_id(tmp, len);
1282   if (!channel_id)
1283     goto out;
1284
1285   /* Get the channel entry */
1286   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1287                                    &id_cache)) {
1288     silc_free(channel_id);
1289     COMMAND_REPLY_ERROR;
1290     goto out;
1291   }
1292   
1293   channel = (SilcChannelEntry)id_cache->context;
1294
1295   /* Get channel mode */
1296   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1297   if (!tmp) {
1298     silc_free(channel_id);
1299     COMMAND_REPLY_ERROR;
1300     goto out;
1301   }
1302
1303   /* Save the mode */
1304   SILC_GET32_MSB(mode, tmp);
1305   channel->mode = mode;
1306
1307   /* Notify application */
1308   COMMAND_REPLY((ARGS, channel, mode));
1309
1310   silc_free(channel_id);
1311
1312  out:
1313   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1314   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1315   silc_client_command_reply_free(cmd);
1316 }
1317
1318 /* Received reply for CUMODE command */
1319
1320 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1321 {
1322   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1323   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1324   SilcCommandStatus status;
1325   SilcIDCacheEntry id_cache = NULL;
1326   SilcClientID *client_id;
1327   SilcChannelID *channel_id;
1328   SilcClientEntry client_entry;
1329   SilcChannelEntry channel;
1330   SilcChannelUser chu;
1331   unsigned char *modev, *tmp, *id;
1332   uint32 len, mode;
1333   
1334   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1335   if (status != SILC_STATUS_OK) {
1336     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1337              "%s", silc_client_command_status_message(status));
1338     COMMAND_REPLY_ERROR;
1339     goto out;
1340   }
1341   
1342   /* Get channel mode */
1343   modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1344   if (!modev) {
1345     COMMAND_REPLY_ERROR;
1346     goto out;
1347   }
1348
1349   /* Take Channel ID */
1350   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1351   if (!tmp)
1352     goto out;
1353   channel_id = silc_id_payload_parse_id(tmp, len);
1354   if (!channel_id)
1355     goto out;
1356
1357   /* Get the channel entry */
1358   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1359                                    &id_cache)) {
1360     silc_free(channel_id);
1361     COMMAND_REPLY_ERROR;
1362     goto out;
1363   }
1364   
1365   channel = (SilcChannelEntry)id_cache->context;
1366
1367   /* Get Client ID */
1368   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1369   if (!id) {
1370     silc_free(channel_id);
1371     COMMAND_REPLY_ERROR;
1372     goto out;
1373   }
1374   client_id = silc_id_payload_parse_id(id, len);
1375   if (!client_id) {
1376     silc_free(channel_id);
1377     COMMAND_REPLY_ERROR;
1378     goto out;
1379   }
1380   
1381   /* Get client entry */
1382   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1383                                        NULL, NULL, 
1384                                        silc_hash_client_id_compare, NULL,
1385                                        &id_cache)) {
1386     silc_free(channel_id);
1387     silc_free(client_id);
1388     COMMAND_REPLY_ERROR;
1389     goto out;
1390   }
1391
1392   client_entry = (SilcClientEntry)id_cache->context;
1393
1394   /* Save the mode */
1395   SILC_GET32_MSB(mode, modev);
1396   silc_list_start(channel->clients);
1397   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1398     if (chu->client == client_entry) {
1399       chu->mode = mode;
1400       break;
1401     }
1402   }
1403
1404   /* Notify application */
1405   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1406   silc_free(client_id);
1407   silc_free(channel_id);
1408   
1409  out:
1410   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1411   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1412   silc_client_command_reply_free(cmd);
1413 }
1414
1415 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1416 {
1417   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1418   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1419   SilcCommandStatus status;
1420   unsigned char *tmp;
1421
1422   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1423   SILC_GET16_MSB(status, tmp);
1424   if (status != SILC_STATUS_OK) {
1425     cmd->client->internal->ops->say(
1426                       cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1427                       "%s", silc_client_command_status_message(status));
1428     COMMAND_REPLY_ERROR;
1429     goto out;
1430   }
1431
1432   /* Notify application */
1433   COMMAND_REPLY((ARGS));
1434
1435  out:
1436   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1437   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1438   silc_client_command_reply_free(cmd);
1439 }
1440
1441 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1442 {
1443   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1444   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1445   SilcCommandStatus status;
1446   unsigned char *tmp;
1447
1448   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1449   SILC_GET16_MSB(status, tmp);
1450   if (status != SILC_STATUS_OK) {
1451     cmd->client->internal->ops->say(
1452                        cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1453                        "%s", silc_client_command_status_message(status));
1454     COMMAND_REPLY_ERROR;
1455     goto out;
1456   }
1457
1458   /* Notify application */
1459   COMMAND_REPLY((ARGS));
1460
1461  out:
1462   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1463   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1464   silc_client_command_reply_free(cmd);
1465 }
1466
1467 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1468 {
1469   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1470   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1471   SilcCommandStatus status;
1472   unsigned char *tmp;
1473
1474   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1475   SILC_GET16_MSB(status, tmp);
1476   if (status != SILC_STATUS_OK) {
1477     cmd->client->internal->ops->say(
1478                        cmd->client, conn, 
1479                        SILC_CLIENT_MESSAGE_ERROR,
1480                        "%s", silc_client_command_status_message(status));
1481     COMMAND_REPLY_ERROR;
1482     goto out;
1483   }
1484
1485   /* Notify application */
1486   COMMAND_REPLY((ARGS));
1487
1488  out:
1489   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1490   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1491   silc_client_command_reply_free(cmd);
1492 }
1493
1494 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1495 {
1496   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1497   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1498   SilcCommandStatus status;
1499   unsigned char *tmp;
1500
1501   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1502   SILC_GET16_MSB(status, tmp);
1503   if (status != SILC_STATUS_OK) {
1504     cmd->client->internal->ops->say(
1505                        cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1506                        "%s", silc_client_command_status_message(status));
1507     COMMAND_REPLY_ERROR;
1508     goto out;
1509   }
1510
1511   /* Notify application */
1512   COMMAND_REPLY((ARGS));
1513
1514  out:
1515   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1516   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1517   silc_client_command_reply_free(cmd);
1518 }
1519
1520 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1521 {
1522   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1523   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1524   SilcCommandStatus status;
1525   SilcIDCacheEntry id_cache = NULL;
1526   SilcChannelEntry channel;
1527   SilcChannelID *channel_id;
1528   unsigned char *tmp;
1529   uint32 len;
1530
1531   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1532   SILC_GET16_MSB(status, tmp);
1533   if (status != SILC_STATUS_OK) {
1534     cmd->client->internal->ops->say(
1535                        cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1536                        "%s", silc_client_command_status_message(status));
1537     COMMAND_REPLY_ERROR;
1538     goto out;
1539   }
1540
1541   /* Take Channel ID */
1542   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1543   if (!tmp)
1544     goto out;
1545
1546   channel_id = silc_id_payload_parse_id(tmp, len);
1547   if (!channel_id)
1548     goto out;
1549
1550   /* Get the channel entry */
1551   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1552                                    &id_cache)) {
1553     silc_free(channel_id);
1554     COMMAND_REPLY_ERROR;
1555     goto out;
1556   }
1557   
1558   channel = (SilcChannelEntry)id_cache->context;
1559
1560   /* Get the ban list */
1561   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1562
1563   /* Notify application */
1564   COMMAND_REPLY((ARGS, channel, tmp));
1565
1566  out:
1567   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1568   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1569   silc_client_command_reply_free(cmd);
1570 }
1571
1572 SILC_CLIENT_CMD_REPLY_FUNC(close)
1573 {
1574   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1575   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1576   SilcCommandStatus status;
1577   unsigned char *tmp;
1578
1579   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1580   SILC_GET16_MSB(status, tmp);
1581   if (status != SILC_STATUS_OK) {
1582     cmd->client->internal->ops->say(
1583                          cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1584                          "%s", silc_client_command_status_message(status));
1585     COMMAND_REPLY_ERROR;
1586     goto out;
1587   }
1588
1589   /* Notify application */
1590   COMMAND_REPLY((ARGS));
1591
1592  out:
1593   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1594   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1595   silc_client_command_reply_free(cmd);
1596 }
1597  
1598 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1599 {
1600   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1601   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1602   SilcCommandStatus status;
1603   unsigned char *tmp;
1604
1605   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1606   SILC_GET16_MSB(status, tmp);
1607   if (status != SILC_STATUS_OK) {
1608     cmd->client->internal->ops->say(
1609                          cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1610                          "%s", silc_client_command_status_message(status));
1611     COMMAND_REPLY_ERROR;
1612     goto out;
1613   }
1614
1615   /* Notify application */
1616   COMMAND_REPLY((ARGS));
1617
1618  out:
1619   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1620   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1621   silc_client_command_reply_free(cmd);
1622 }
1623  
1624 /* Reply to LEAVE command. */
1625
1626 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1627 {
1628   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1629   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1630   SilcCommandStatus status;
1631   unsigned char *tmp;
1632
1633   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1634   SILC_GET16_MSB(status, tmp);
1635   if (status != SILC_STATUS_OK) {
1636     cmd->client->internal->ops->say(
1637                           cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1638                           "%s", silc_client_command_status_message(status));
1639     COMMAND_REPLY_ERROR;
1640     goto out;
1641   }
1642
1643   /* Notify application */
1644   COMMAND_REPLY((ARGS));
1645
1646  out:
1647   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1648   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1649   silc_client_command_reply_free(cmd);
1650 }
1651
1652 /* Reply to USERS command. Received list of client ID's and theirs modes
1653    on the channel we requested. */
1654
1655 SILC_CLIENT_CMD_REPLY_FUNC(users)
1656 {
1657   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1658   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1659   SilcCommandStatus status;
1660   SilcIDCacheEntry id_cache = NULL;
1661   SilcChannelEntry channel;
1662   SilcChannelUser chu;
1663   SilcChannelID *channel_id = NULL;
1664   SilcBuffer client_id_list = NULL;
1665   SilcBuffer client_mode_list = NULL;
1666   unsigned char *tmp;
1667   uint32 tmp_len, list_count;
1668   int i;
1669   unsigned char **res_argv = NULL;
1670   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1671
1672   SILC_LOG_DEBUG(("Start"));
1673
1674   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1675   SILC_GET16_MSB(status, tmp);
1676   if (status != SILC_STATUS_OK) {
1677     cmd->client->internal->ops->say(
1678                           cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1679                           "%s", silc_client_command_status_message(status));
1680     COMMAND_REPLY_ERROR;
1681     goto out;
1682   }
1683
1684   /* Get channel ID */
1685   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1686   if (!tmp) {
1687     COMMAND_REPLY_ERROR;
1688     goto out;
1689   }
1690   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1691   if (!channel_id) {
1692     COMMAND_REPLY_ERROR;
1693     goto out;
1694   }
1695   
1696   /* Get the list count */
1697   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1698   if (!tmp) {
1699     COMMAND_REPLY_ERROR;
1700     goto out;
1701   }
1702   SILC_GET32_MSB(list_count, tmp);
1703
1704   /* Get Client ID list */
1705   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1706   if (!tmp) {
1707     COMMAND_REPLY_ERROR;
1708     goto out;
1709   }
1710
1711   client_id_list = silc_buffer_alloc(tmp_len);
1712   silc_buffer_pull_tail(client_id_list, tmp_len);
1713   silc_buffer_put(client_id_list, tmp, tmp_len);
1714
1715   /* Get client mode list */
1716   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1717   if (!tmp) {
1718     COMMAND_REPLY_ERROR;
1719     goto out;
1720   }
1721
1722   client_mode_list = silc_buffer_alloc(tmp_len);
1723   silc_buffer_pull_tail(client_mode_list, tmp_len);
1724   silc_buffer_put(client_mode_list, tmp, tmp_len);
1725
1726   /* Get channel entry */
1727   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1728                                    &id_cache)) {
1729     /* Resolve the channel from server */
1730     silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE);
1731     
1732     /* Register pending command callback. After we've received the channel
1733        information we will reprocess this command reply by re-calling this
1734        USERS command reply callback. */
1735     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1736                                 NULL, silc_client_command_reply_users, cmd);
1737     return;
1738   } else {
1739     channel = (SilcChannelEntry)id_cache->context;
1740   }
1741
1742   /* Remove old client list from channel. */
1743   silc_list_start(channel->clients);
1744   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1745     silc_list_del(channel->clients, chu);
1746     silc_free(chu);
1747   }
1748
1749   /* Cache the received Client ID's and modes. */
1750   for (i = 0; i < list_count; i++) {
1751     uint16 idp_len;
1752     uint32 mode;
1753     SilcClientID *client_id;
1754     SilcClientEntry client;
1755
1756     /* Client ID */
1757     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1758     idp_len += 4;
1759     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1760     if (!client_id)
1761       continue;
1762
1763     /* Mode */
1764     SILC_GET32_MSB(mode, client_mode_list->data);
1765
1766     /* Check if we have this client cached already. */
1767     id_cache = NULL;
1768     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1769                                     (void *)client_id, 
1770                                     NULL, NULL, 
1771                                     silc_hash_client_id_compare, NULL,
1772                                     &id_cache);
1773
1774     if (!id_cache || !((SilcClientEntry)id_cache->context)->username ||
1775         !((SilcClientEntry)id_cache->context)->realname) {
1776
1777       if (id_cache && id_cache->context) {
1778         SilcClientEntry client_entry = (SilcClientEntry)id_cache->context;
1779         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
1780           silc_buffer_pull(client_id_list, idp_len);
1781           silc_buffer_pull(client_mode_list, 4);
1782           continue;
1783         }
1784         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
1785       }
1786
1787       /* No we don't have it (or it is incomplete in information), query
1788          it from the server. Assemble argument table that will be sent
1789          for the WHOIS command later. */
1790       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1791                               (res_argc + 1));
1792       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1793                                    (res_argc + 1));
1794       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1795                                     (res_argc + 1));
1796       res_argv[res_argc] = client_id_list->data;
1797       res_argv_lens[res_argc] = idp_len;
1798       res_argv_types[res_argc] = res_argc + 3;
1799       res_argc++;
1800     } else {
1801       /* Found the client, join it to the channel */
1802       client = (SilcClientEntry)id_cache->context;
1803       chu = silc_calloc(1, sizeof(*chu));
1804       chu->client = client;
1805       chu->mode = mode;
1806       silc_list_add(channel->clients, chu);
1807
1808       silc_free(client_id);
1809       id_cache = NULL;
1810     }
1811
1812     silc_buffer_pull(client_id_list, idp_len);
1813     silc_buffer_pull(client_mode_list, 4);
1814   }
1815
1816   /* Query the client information from server if the list included clients
1817      that we don't know about. */
1818   if (res_argc) {
1819     SilcBuffer res_cmd;
1820
1821     /* Send the WHOIS command to server */
1822     silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1823                                  silc_client_command_reply_whois_i, 0,
1824                                  ++conn->cmd_ident);
1825     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1826                                           res_argc, res_argv, res_argv_lens,
1827                                           res_argv_types, conn->cmd_ident);
1828     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1829                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1830                             TRUE);
1831
1832     /* Register pending command callback. After we've received the WHOIS
1833        command reply we will reprocess this command reply by re-calling this
1834        USERS command reply callback. */
1835     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1836                                 NULL, silc_client_command_reply_users, cmd);
1837
1838     silc_buffer_free(res_cmd);
1839     if (channel_id)
1840       silc_free(channel_id);
1841
1842     silc_free(res_argv);
1843     silc_free(res_argv_lens);
1844     silc_free(res_argv_types);
1845     return;
1846   }
1847
1848   /* Notify application */
1849   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1850
1851  out:
1852   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1853   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1854   silc_client_command_reply_free(cmd);
1855   silc_free(channel_id);
1856   if (client_id_list)
1857     silc_buffer_free(client_id_list);
1858   if (client_mode_list)
1859     silc_buffer_free(client_mode_list);
1860 }
1861
1862 /* Received command reply to GETKEY command. WE've received the remote
1863    client's public key. */
1864
1865 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1866 {
1867   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1868   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1869   SilcCommandStatus status;
1870   SilcIDCacheEntry id_cache;
1871   SilcIDPayload idp = NULL;
1872   SilcClientID *client_id = NULL;
1873   SilcClientEntry client_entry;
1874   SilcServerID *server_id = NULL;
1875   SilcServerEntry server_entry;
1876   SilcSKEPKType type;
1877   unsigned char *tmp, *pk;
1878   uint32 len;
1879   uint16 pk_len;
1880   SilcIdType id_type;
1881   SilcPublicKey public_key = NULL;
1882
1883   SILC_LOG_DEBUG(("Start"));
1884
1885   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1886   SILC_GET16_MSB(status, tmp);
1887   if (status != SILC_STATUS_OK) {
1888     cmd->client->internal->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1889                           "%s", silc_client_command_status_message(status));
1890     COMMAND_REPLY_ERROR;
1891     goto out;
1892   }
1893
1894   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1895   if (!tmp) {
1896     COMMAND_REPLY_ERROR;
1897     goto out;
1898   }
1899   idp = silc_id_payload_parse(tmp, len);
1900   if (!idp) {
1901     COMMAND_REPLY_ERROR;
1902     goto out;
1903   }
1904
1905   /* Get the public key payload */
1906   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1907   if (tmp) {
1908     /* Decode the public key */
1909     SILC_GET16_MSB(pk_len, tmp);
1910     SILC_GET16_MSB(type, tmp + 2);
1911     pk = tmp + 4;
1912     
1913     if (type != SILC_SKE_PK_TYPE_SILC) {
1914       COMMAND_REPLY_ERROR;
1915       goto out;
1916     }
1917     
1918     if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) {
1919       COMMAND_REPLY_ERROR;
1920       goto out;
1921     }
1922   } 
1923    
1924   id_type = silc_id_payload_get_type(idp);
1925   if (id_type == SILC_ID_CLIENT) {
1926     /* Received client's public key */
1927     client_id = silc_id_payload_get_id(idp);
1928     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1929                                          (void *)client_id, 
1930                                          NULL, NULL, 
1931                                          silc_hash_client_id_compare, NULL,
1932                                          &id_cache)) {
1933       COMMAND_REPLY_ERROR;
1934       goto out;
1935     }
1936
1937     client_entry = (SilcClientEntry)id_cache->context;
1938
1939     /* Notify application */
1940     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1941   } else if (id_type == SILC_ID_SERVER) {
1942     /* Received server's public key */
1943     server_id = silc_id_payload_get_id(idp);
1944     if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
1945                                      &id_cache)) {
1946       COMMAND_REPLY_ERROR;
1947       goto out;
1948     }
1949
1950     server_entry = (SilcServerEntry)id_cache->context;
1951
1952     /* Notify application */
1953     COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1954   }
1955
1956  out:
1957   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1958   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1959   if (idp)
1960     silc_id_payload_free(idp);
1961   if (public_key)
1962     silc_pkcs_public_key_free(public_key);
1963   silc_free(client_id);
1964   silc_free(server_id);
1965   silc_client_command_reply_free(cmd);
1966 }
1967
1968 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1969 {
1970   silc_client_command_reply_free(context);
1971 }
1972
1973
1974 /******************************************************************************
1975
1976                       Internal command reply functions
1977
1978 ******************************************************************************/
1979
1980 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1981 {
1982   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1983   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1984   SilcCommandStatus status;
1985
1986   SILC_LOG_DEBUG(("Start"));
1987
1988   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1989   if (status != SILC_STATUS_OK &&
1990       status != SILC_STATUS_LIST_START &&
1991       status != SILC_STATUS_LIST_ITEM &&
1992       status != SILC_STATUS_LIST_END)
1993     goto out;
1994
1995   /* Save WHOIS info */
1996   silc_client_command_reply_whois_save(cmd, status, FALSE);
1997
1998   /* Pending callbacks are not executed if this was an list entry */
1999   if (status != SILC_STATUS_OK &&
2000       status != SILC_STATUS_LIST_END) {
2001     silc_client_command_reply_free(cmd);
2002     return;
2003   }
2004
2005  out:
2006   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
2007   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
2008
2009   /* If we received notify for invalid ID we'll remove the ID if we
2010      have it cached. */
2011   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2012     SilcClientEntry client_entry;
2013     uint32 tmp_len;
2014     unsigned char *tmp =
2015       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2016                                  2, &tmp_len);
2017     if (tmp) {
2018       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
2019       if (client_id) {
2020         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2021                                                     client_id);
2022         if (client_entry)
2023           silc_client_del_client(cmd->client, conn, client_entry);
2024         silc_free(client_id);
2025       }
2026     }
2027   }
2028
2029   /* Unregister this command reply */
2030   silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
2031                                  NULL, silc_client_command_reply_whois_i,
2032                                  cmd->ident);
2033
2034   silc_client_command_reply_free(cmd);
2035 }
2036
2037 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
2038 {
2039   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2040   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2041   SilcCommandStatus status;
2042
2043   SILC_LOG_DEBUG(("Start"));
2044
2045   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
2046   if (status != SILC_STATUS_OK &&
2047       status != SILC_STATUS_LIST_START &&
2048       status != SILC_STATUS_LIST_ITEM &&
2049       status != SILC_STATUS_LIST_END)
2050     goto out;
2051
2052   /* Save IDENTIFY info */
2053   silc_client_command_reply_identify_save(cmd, status, FALSE);
2054
2055   /* Pending callbacks are not executed if this was an list entry */
2056   if (status != SILC_STATUS_OK &&
2057       status != SILC_STATUS_LIST_END) {
2058     silc_client_command_reply_free(cmd);
2059     return;
2060   }
2061
2062  out:
2063   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
2064   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
2065
2066   /* If we received notify for invalid ID we'll remove the ID if we
2067      have it cached. */
2068   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2069     SilcClientEntry client_entry;
2070     uint32 tmp_len;
2071     unsigned char *tmp =
2072       silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2073                                  2, &tmp_len);
2074     if (tmp) {
2075       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
2076       if (client_id) {
2077         client_entry = silc_client_get_client_by_id(cmd->client, conn,
2078                                                     client_id);
2079         if (client_entry)
2080           silc_client_del_client(cmd->client, conn, client_entry);
2081         silc_free(client_id);
2082       }
2083     }
2084   }
2085
2086   /* Unregister this command reply */
2087   silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
2088                                  NULL, silc_client_command_reply_identify_i,
2089                                  cmd->ident);
2090
2091   silc_client_command_reply_free(cmd);
2092 }
2093
2094 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2095 {
2096   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2097   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2098   SilcCommandStatus status;
2099   unsigned char *tmp;
2100   SilcIDCacheEntry id_cache;
2101   SilcServerEntry server;
2102   SilcServerID *server_id = NULL;
2103   char *server_name, *server_info;
2104   uint32 len;
2105
2106   SILC_LOG_DEBUG(("Start"));
2107
2108   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
2109   SILC_GET16_MSB(status, tmp);
2110   if (status != SILC_STATUS_OK)
2111     goto out;
2112
2113   /* Get server ID */
2114   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2115   if (!tmp)
2116     goto out;
2117
2118   server_id = silc_id_payload_parse_id(tmp, len);
2119   if (!server_id)
2120     goto out;
2121
2122   /* Get server name */
2123   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2124   if (!server_name)
2125     goto out;
2126
2127   /* Get server info */
2128   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2129   if (!server_info)
2130     goto out;
2131
2132   /* See whether we have this server cached. If not create it. */
2133   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
2134                                    &id_cache)) {
2135     SILC_LOG_DEBUG(("New server entry"));
2136     server = silc_calloc(1, sizeof(*server));
2137     server->server_name = strdup(server_name);
2138     server->server_info = strdup(server_info);
2139     server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
2140
2141     /* Add it to the cache */
2142     silc_idcache_add(conn->server_cache, server->server_name,
2143                      server->server_id, (void *)server, 0, NULL);
2144   }
2145   
2146  out:
2147   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2148   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
2149   silc_free(server_id);
2150   silc_client_command_reply_free(cmd);
2151 }