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