updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
40 {
41   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44   SILC_CLIENT_CMD_REPLY(nick, NICK),
45   SILC_CLIENT_CMD_REPLY(list, LIST),
46   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47   SILC_CLIENT_CMD_REPLY(invite, INVITE),
48   SILC_CLIENT_CMD_REPLY(kill, KILL),
49   SILC_CLIENT_CMD_REPLY(info, INFO),
50   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
51   SILC_CLIENT_CMD_REPLY(ping, PING),
52   SILC_CLIENT_CMD_REPLY(oper, OPER),
53   SILC_CLIENT_CMD_REPLY(join, JOIN),
54   SILC_CLIENT_CMD_REPLY(motd, MOTD),
55   SILC_CLIENT_CMD_REPLY(umode, UMODE),
56   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
57   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
58   SILC_CLIENT_CMD_REPLY(kick, KICK),
59   SILC_CLIENT_CMD_REPLY(restart, RESTART),
60   SILC_CLIENT_CMD_REPLY(close, CLOSE),
61   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
62   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
63   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
64   SILC_CLIENT_CMD_REPLY(users, USERS),
65
66   { NULL, 0 },
67 };
68
69 /* Status message structure. Messages are defined below. */
70 typedef struct {
71   SilcCommandStatus status;
72   char *message;
73 } SilcCommandStatusMessage;
74
75 /* Status messages returned by the server */
76 #define STAT(x) SILC_STATUS_ERR_##x
77 const SilcCommandStatusMessage silc_command_status_messages[] = {
78
79   { STAT(NO_SUCH_NICK),      "No such nickname" },
80   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
81   { STAT(NO_SUCH_SERVER),    "No such server" },
82   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
83   { STAT(NO_RECIPIENT),      "No recipient given" },
84   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
85   { STAT(WILDCARDS),         "Unknown command" },
86   { STAT(NO_CLIENT_ID),      "No Client ID given" },
87   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
88   { STAT(NO_SERVER_ID),      "No Server ID given" },
89   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
90   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
91   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
92   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
93   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
94   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
95   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
96   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
97   { STAT(NOT_REGISTERED),    "You have not registered" },
98   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
99   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
100   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
101   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
102   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
103   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
104   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
105   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
106   { STAT(UNKNOWN_MODE),    "Unknown mode" },
107   { STAT(NOT_YOU),         "Cannot change mode for other users" },
108   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
109   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
110   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
111   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
112   { STAT(BAD_NICKNAME),    "Bad nickname" },
113   { STAT(BAD_CHANNEL),     "Bad channel name" },
114   { STAT(AUTH_FAILED),     "Authentication failed" },
115
116   { 0, NULL }
117 };
118
119 /* Command reply operation that is called at the end of all command replys. 
120    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123              cmd->payload, TRUE, silc_command_get(cmd->payload), status
124
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127   cmd->sock->user_data, cmd->payload, FALSE, \
128   silc_command_get(cmd->payload), status)
129
130 /* Process received command reply. */
131
132 void silc_client_command_reply_process(SilcClient client,
133                                        SilcSocketConnection sock,
134                                        SilcPacketContext *packet)
135 {
136   SilcBuffer buffer = packet->buffer;
137   SilcClientCommandReply *cmd;
138   SilcClientCommandReplyContext ctx;
139   SilcCommandPayload payload;
140   SilcCommand command;
141   unsigned short ident;
142
143   /* Get command reply payload from packet */
144   payload = silc_command_payload_parse(buffer);
145   if (!payload) {
146     /* Silently ignore bad reply packet */
147     SILC_LOG_DEBUG(("Bad command reply packet"));
148     return;
149   }
150   
151   /* Allocate command reply context. This must be free'd by the
152      command reply routine receiving it. */
153   ctx = silc_calloc(1, sizeof(*ctx));
154   ctx->client = client;
155   ctx->sock = sock;
156   ctx->payload = payload;
157   ctx->args = silc_command_get_args(ctx->payload);
158   ctx->packet = packet;
159   ident = silc_command_get_ident(ctx->payload);
160       
161   /* Check for pending commands and mark to be exeucted */
162   silc_client_command_pending_check(sock->user_data, ctx, 
163                                     silc_command_get(ctx->payload), ident);
164
165   /* Execute command reply */
166   command = silc_command_get(ctx->payload);
167   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168     if (cmd->cmd == command)
169       break;
170
171   if (cmd == NULL || !cmd->cb) {
172     silc_free(ctx);
173     return;
174   }
175
176   cmd->cb(ctx);
177 }
178
179 /* Returns status message string */
180
181 static char *
182 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_free_payload(cmd->payload);
203     silc_free(cmd);
204   }
205 }
206
207 static void 
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209                                       SilcCommandStatus status)
210 {
211   char buf[256];
212   int argc, len;
213   unsigned char *id_data;
214   char *nickname = NULL, *username = NULL;
215   char *realname = NULL;
216   SilcClientID *client_id;
217   SilcIDCacheEntry id_cache = NULL;
218   SilcClientEntry client_entry = NULL;
219   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
220   
221   memset(buf, 0, sizeof(buf));
222   
223   argc = silc_argument_get_arg_num(cmd->args);
224
225   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
226   if (!id_data) {
227     COMMAND_REPLY_ERROR;
228     return;
229   }
230   
231   client_id = silc_id_payload_parse_id(id_data, len);
232   if (!client_id) {
233     COMMAND_REPLY_ERROR;
234     return;
235   }
236   
237   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
238   if (nickname) {
239     strncat(buf, nickname, len);
240     strncat(buf, " is ", 4);
241   }
242   
243   username = silc_argument_get_arg_type(cmd->args, 4, &len);
244   if (username) {
245     strncat(buf, username, len);
246   }
247   
248   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
249   if (realname) {
250     strncat(buf, " (", 2);
251     strncat(buf, realname, len);
252     strncat(buf, ")", 1);
253   }
254   
255   /* Check if we have this client cached already. */
256   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257                                    SILC_ID_CLIENT, &id_cache)) {
258     client_entry = silc_calloc(1, sizeof(*client_entry));
259     client_entry->id = client_id;
260     silc_parse_nickname(nickname, &client_entry->nickname, 
261                         &client_entry->server, &client_entry->num);
262     client_entry->username = strdup(username);
263     if (realname)
264       client_entry->realname = strdup(realname);
265     
266     /* Add client to cache */
267     silc_idcache_add(conn->client_cache, client_entry->nickname,
268                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
269   } else {
270     client_entry = (SilcClientEntry)id_cache->context;
271     if (client_entry->nickname)
272       silc_free(client_entry->nickname);
273     if (client_entry->server)
274       silc_free(client_entry->server);
275     if (client_entry->username)
276       silc_free(client_entry->username);
277     if (client_entry->realname)
278       silc_free(client_entry->realname);
279
280     silc_parse_nickname(nickname, &client_entry->nickname, 
281                         &client_entry->server, &client_entry->num);
282     client_entry->username = strdup(username);
283     if (realname)
284       client_entry->realname = strdup(realname);
285
286     id_cache->data = client_entry->nickname;
287     silc_idcache_sort_by_data(conn->client_cache);
288
289     silc_free(client_id);
290   }
291
292   if (!cmd->callback)
293     cmd->client->ops->say(cmd->client, conn, "%s", buf);
294
295   /* Notify application */
296   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
297                  NULL, NULL));
298 }
299
300 /* Received reply for WHOIS command. This maybe called several times
301    for one WHOIS command as server may reply with list of results. */
302
303 SILC_CLIENT_CMD_REPLY_FUNC(whois)
304 {
305   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
306   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
307   SilcCommandStatus status;
308   unsigned char *tmp;
309
310   SILC_LOG_DEBUG(("Start"));
311
312   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
313   SILC_GET16_MSB(status, tmp);
314   if (status != SILC_STATUS_OK && 
315       status != SILC_STATUS_LIST_START &&
316       status != SILC_STATUS_LIST_ITEM &&
317       status != SILC_STATUS_LIST_END) {
318     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
319       /* Take nickname which may be provided */
320       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
321       if (tmp)
322         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
323                  silc_client_command_status_message(status));
324       else
325         cmd->client->ops->say(cmd->client, conn, "%s",
326                  silc_client_command_status_message(status));
327       COMMAND_REPLY_ERROR;
328       goto out;
329     } else {
330       cmd->client->ops->say(cmd->client, conn,
331                "%s", silc_client_command_status_message(status));
332       COMMAND_REPLY_ERROR;
333       goto out;
334     }
335   }
336
337   /* Display one whois reply */
338   if (status == SILC_STATUS_OK) {
339     silc_client_command_reply_whois_print(cmd, status);
340   }
341
342   /* XXX list should not be displayed untill all items has been received. */
343   if (status == SILC_STATUS_LIST_START) {
344     silc_client_command_reply_whois_print(cmd, status);
345   }
346
347   if (status == SILC_STATUS_LIST_ITEM) {
348     silc_client_command_reply_whois_print(cmd, status);
349   }
350
351   if (status == SILC_STATUS_LIST_END) {
352     silc_client_command_reply_whois_print(cmd, status);
353   }
354
355   /* Execute any pending command callbacks */
356   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
357
358  out:
359   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
360   silc_client_command_reply_free(cmd);
361 }
362
363 /* Received reply for WHOWAS command. */
364
365 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
366 {
367
368 }
369
370 /* Received reply for IDENTIFY command. This maybe called several times
371    for one IDENTIFY command as server may reply with list of results. 
372    This is totally silent and does not print anything on screen. */
373
374 SILC_CLIENT_CMD_REPLY_FUNC(identify)
375 {
376   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
377   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
378   SilcClientEntry client_entry;
379   SilcIDCacheEntry id_cache = NULL;
380   SilcCommandStatus status;
381   unsigned char *tmp;
382
383   SILC_LOG_DEBUG(("Start"));
384
385   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
386   SILC_GET16_MSB(status, tmp);
387   if (status != SILC_STATUS_OK) {
388     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
389       /* Take nickname which may be provided */
390       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
391       if (tmp)
392         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
393                  silc_client_command_status_message(status));
394       else
395         cmd->client->ops->say(cmd->client, conn, "%s",
396                  silc_client_command_status_message(status));
397       COMMAND_REPLY_ERROR;
398       goto out;
399     } else {
400       cmd->client->ops->say(cmd->client, conn,
401                "%s", silc_client_command_status_message(status));
402       COMMAND_REPLY_ERROR;
403       goto out;
404     }
405   }
406
407   /* Display one whois reply */
408   if (status == SILC_STATUS_OK) {
409     unsigned int len;
410     unsigned char *id_data;
411     char *nickname;
412     char *username;
413     SilcClientID *client_id;
414
415     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
416     if (!id_data)
417       goto out;
418     client_id = silc_id_payload_parse_id(id_data, len);
419     if (!client_id)
420       goto out;
421
422     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
423     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
424
425     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
426                                      SILC_ID_CLIENT, &id_cache)) {
427       client_entry = silc_calloc(1, sizeof(*client_entry));
428       client_entry->id = client_id;
429       silc_parse_nickname(nickname, &client_entry->nickname, 
430                           &client_entry->server, &client_entry->num);
431       if (username)
432         client_entry->username = strdup(username);
433     
434       /* Add client to cache */
435       silc_idcache_add(conn->client_cache, client_entry->nickname,
436                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
437     } else {
438       client_entry = (SilcClientEntry)id_cache->context;
439       if (client_entry->nickname)
440         silc_free(client_entry->nickname);
441       if (client_entry->server)
442         silc_free(client_entry->server);
443       if (username && client_entry->username)
444         silc_free(client_entry->username);
445
446       silc_parse_nickname(nickname, &client_entry->nickname, 
447                           &client_entry->server, &client_entry->num);
448
449       if (username)
450         client_entry->username = strdup(username);
451
452       id_cache->data = client_entry->nickname;
453       silc_idcache_sort_by_data(conn->client_cache);
454     
455       silc_free(client_id);
456     }
457   }
458
459   if (status == SILC_STATUS_LIST_START) {
460
461   }
462
463   if (status == SILC_STATUS_LIST_END) {
464
465   }
466
467   /* Execute any pending command callbacks */
468   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
469
470  out:
471   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
472   silc_client_command_reply_free(cmd);
473 }
474
475 /* Received reply for command NICK. If everything went without errors
476    we just received our new Client ID. */
477
478 SILC_CLIENT_CMD_REPLY_FUNC(nick)
479 {
480   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
481   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
482   SilcCommandStatus status;
483   SilcIDPayload idp;
484   unsigned char *tmp;
485   unsigned int argc, len;
486
487   SILC_LOG_DEBUG(("Start"));
488
489   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
490   if (status != SILC_STATUS_OK) {
491     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
492              silc_client_command_status_message(status));
493     COMMAND_REPLY_ERROR;
494     goto out;
495   }
496
497   argc = silc_argument_get_arg_num(cmd->args);
498   if (argc < 2 || argc > 2) {
499     cmd->client->ops->say(cmd->client, conn, 
500                           "Cannot set nickname: bad reply to command");
501     COMMAND_REPLY_ERROR;
502     goto out;
503   }
504
505   /* Take received Client ID */
506   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
507   idp = silc_id_payload_parse_data(tmp, len);
508   if (!idp) {
509     COMMAND_REPLY_ERROR;
510     goto out;
511   }
512   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
513     
514   /* Notify application */
515   COMMAND_REPLY((ARGS, conn->local_entry));
516
517   /* Execute any pending command callbacks */
518   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
519
520  out:
521   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
522   silc_client_command_reply_free(cmd);
523 }
524
525 SILC_CLIENT_CMD_REPLY_FUNC(list)
526 {
527 }
528
529 /* Received reply to topic command. */
530
531 SILC_CLIENT_CMD_REPLY_FUNC(topic)
532 {
533   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
534   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
535   SilcCommandStatus status;
536   SilcChannelEntry channel;
537   SilcChannelID *channel_id = NULL;
538   SilcIDCacheEntry id_cache = NULL;
539   unsigned char *tmp;
540   char *topic;
541   unsigned int argc, len;
542
543   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
544   if (status != SILC_STATUS_OK) {
545     cmd->client->ops->say(cmd->client, conn,
546              "%s", silc_client_command_status_message(status));
547     COMMAND_REPLY_ERROR;
548     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
549     silc_client_command_reply_free(cmd);
550     return;
551   }
552
553   argc = silc_argument_get_arg_num(cmd->args);
554   if (argc < 1 || argc > 3) {
555     COMMAND_REPLY_ERROR;
556     goto out;
557   }
558
559   /* Take Channel ID */
560   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
561   if (!tmp)
562     goto out;
563
564   /* Take topic */
565   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
566   if (!topic)
567     goto out;
568
569   channel_id = silc_id_payload_parse_id(tmp, len);
570   if (!channel_id)
571     goto out;
572
573   /* Get the channel name */
574   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
575                                    SILC_ID_CHANNEL, &id_cache)) {
576     silc_free(channel_id);
577     COMMAND_REPLY_ERROR;
578     goto out;
579   }
580   
581   channel = (SilcChannelEntry)id_cache->context;
582
583   cmd->client->ops->say(cmd->client, conn, 
584                         "Topic on channel %s: %s", channel->channel_name,
585                         topic);
586
587   /* Notify application */
588   COMMAND_REPLY((ARGS, channel, topic));
589
590   /* Execute any pending command callbacks */
591   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
592
593  out:
594   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
595   silc_client_command_reply_free(cmd);
596 }
597
598 /* Received reply to invite command. */
599
600 SILC_CLIENT_CMD_REPLY_FUNC(invite)
601 {
602   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
603   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
604   SilcCommandStatus status;
605   unsigned char *tmp;
606
607   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
608   SILC_GET16_MSB(status, tmp);
609   if (status != SILC_STATUS_OK) {
610     cmd->client->ops->say(cmd->client, conn,
611              "%s", silc_client_command_status_message(status));
612     COMMAND_REPLY_ERROR;
613     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
614     silc_client_command_reply_free(cmd);
615     return;
616   }
617
618   /* Notify application */
619   COMMAND_REPLY((ARGS));
620
621   /* Execute any pending command callbacks */
622   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
623
624   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
625   silc_client_command_reply_free(cmd);
626 }
627  
628 SILC_CLIENT_CMD_REPLY_FUNC(kill)
629 {
630 }
631
632 /* Received reply to INFO command. We receive the server ID and some
633    information about the server user requested. */
634
635 SILC_CLIENT_CMD_REPLY_FUNC(info)
636 {
637   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
638   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
639   SilcClient client = cmd->client;
640   SilcCommandStatus status;
641   unsigned char *tmp;
642
643   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
644   SILC_GET16_MSB(status, tmp);
645   if (status != SILC_STATUS_OK) {
646     cmd->client->ops->say(cmd->client, conn,
647              "%s", silc_client_command_status_message(status));
648     COMMAND_REPLY_ERROR;
649     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
650     silc_client_command_reply_free(cmd);
651     return;
652   }
653
654   /* Get server ID */
655   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
656   if (!tmp)
657     goto out;
658
659   /* XXX save server id */
660
661   /* Get server info */
662   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
663   if (!tmp)
664     goto out;
665
666   client->ops->say(cmd->client, conn, "Info: %s", tmp);
667
668   /* Notify application */
669   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
670
671   /* Execute any pending command callbacks */
672   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
673
674  out:
675   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
676   silc_client_command_reply_free(cmd);
677 }
678
679 /* Received reply to PING command. The reply time is shown to user. */
680
681 SILC_CLIENT_CMD_REPLY_FUNC(ping)
682 {
683   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
684   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
685   SilcCommandStatus status;
686   void *id;
687   int i;
688   time_t diff, curtime;
689
690   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
691   if (status != SILC_STATUS_OK) {
692     cmd->client->ops->say(cmd->client, conn,
693              "%s", silc_client_command_status_message(status));
694     COMMAND_REPLY_ERROR;
695     goto out;
696   }
697
698   curtime = time(NULL);
699   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
700                       cmd->packet->src_id_type);
701   if (!id) {
702     COMMAND_REPLY_ERROR;
703     goto out;
704   }
705
706   for (i = 0; i < conn->ping_count; i++) {
707     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
708       diff = curtime - conn->ping[i].start_time;
709       cmd->client->ops->say(cmd->client, conn, 
710                             "Ping reply from %s: %d second%s", 
711                             conn->ping[i].dest_name, diff, 
712                             diff == 1 ? "" : "s");
713       
714       conn->ping[i].start_time = 0;
715       silc_free(conn->ping[i].dest_id);
716       conn->ping[i].dest_id = NULL;
717       silc_free(conn->ping[i].dest_name);
718       conn->ping[i].dest_name = NULL;
719       break;
720     }
721   }
722
723   silc_free(id);
724
725   /* Notify application */
726   COMMAND_REPLY((ARGS));
727
728   /* Execute any pending command callbacks */
729   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
730
731  out:
732   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
733   silc_client_command_reply_free(cmd);
734 }
735
736 /* Received reply for JOIN command. */
737
738 SILC_CLIENT_CMD_REPLY_FUNC(join)
739 {
740   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
741   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
742   SilcClient client = cmd->client;
743   SilcCommandStatus status;
744   SilcIDPayload idp = NULL;
745   unsigned int argc, mode, len;
746   char *topic, *tmp, *channel_name = NULL;
747   SilcBuffer keyp;
748
749   SILC_LOG_DEBUG(("Start"));
750
751   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
752   if (status != SILC_STATUS_OK) {
753     cmd->client->ops->say(cmd->client, conn,
754              "%s", silc_client_command_status_message(status));
755     COMMAND_REPLY_ERROR;
756     goto out;
757   }
758
759   argc = silc_argument_get_arg_num(cmd->args);
760   if (argc < 3 || argc > 9) {
761     cmd->client->ops->say(cmd->client, conn,
762              "Cannot join channel: Bad reply packet");
763     COMMAND_REPLY_ERROR;
764     goto out;
765   }
766
767   /* Get channel name */
768   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
769   if (!tmp) {
770     cmd->client->ops->say(cmd->client, conn, 
771                           "Cannot join channel: Bad reply packet");
772     COMMAND_REPLY_ERROR;
773     goto out;
774   }
775   channel_name = strdup(tmp);
776
777   /* Get Channel ID */
778   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
779   if (!tmp) {
780     cmd->client->ops->say(cmd->client, conn, 
781                           "Cannot join channel: Bad reply packet");
782     COMMAND_REPLY_ERROR;
783     silc_free(channel_name);
784     goto out;
785   }
786   idp = silc_id_payload_parse_data(tmp, len);
787   if (!idp) {
788     COMMAND_REPLY_ERROR;
789     silc_free(channel_name);
790     goto out;
791   }
792
793   /* Get channel mode */
794   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
795   if (tmp)
796     SILC_GET32_MSB(mode, tmp);
797   else
798     mode = 0;
799
800   /* Get channel key */
801   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
802   if (!tmp) {
803     silc_id_payload_free(idp);
804     silc_free(channel_name);
805     goto out;
806   }
807   keyp = silc_buffer_alloc(len);
808   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
809   silc_buffer_put(keyp, tmp, len);
810
811   /* Get topic */
812   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
813
814   /* Save received Channel ID */
815   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
816                              mode, idp);
817   silc_id_payload_free(idp);
818
819   /* Save channel key */
820   silc_client_save_channel_key(conn, keyp, conn->current_channel);
821   silc_buffer_free(keyp);
822
823   if (topic)
824     client->ops->say(cmd->client, conn, 
825                      "Topic for %s: %s", channel_name, topic);
826
827   /* Notify application */
828   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
829                  NULL, NULL, topic));
830
831   /* Execute any pending command callbacks */
832   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
833
834  out:
835   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
836   silc_client_command_reply_free(cmd);
837 }
838
839 /* Received reply for MOTD command */
840
841 SILC_CLIENT_CMD_REPLY_FUNC(motd)
842 {
843   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
844   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
845   SilcCommandStatus status;
846   unsigned int argc, i;
847   unsigned char *tmp;
848   char *motd = NULL, *cp, line[256];
849
850   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
851   SILC_GET16_MSB(status, tmp);
852   if (status != SILC_STATUS_OK) {
853     cmd->client->ops->say(cmd->client, conn,
854              "%s", silc_client_command_status_message(status));
855     COMMAND_REPLY_ERROR;
856     return;
857   }
858
859   argc = silc_argument_get_arg_num(cmd->args);
860   if (argc > 2) {
861     COMMAND_REPLY_ERROR;
862     goto out;
863   }
864
865   if (argc == 2) {
866     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
867     if (!motd) {
868       COMMAND_REPLY_ERROR;
869       goto out;
870     }
871
872     i = 0;
873     cp = motd;
874     while(cp[i] != 0) {
875       if (cp[i++] == '\n') {
876         memset(line, 0, sizeof(line));
877         strncat(line, cp, i - 1);
878         cp += i;
879         
880         if (i == 2)
881           line[0] = ' ';
882         
883         cmd->client->ops->say(cmd->client, conn, "%s", line);
884         
885         if (!strlen(cp))
886           break;
887         i = 0;
888       }
889     }
890   }
891
892   /* Notify application */
893   COMMAND_REPLY((ARGS, motd));
894
895   /* Execute any pending command callbacks */
896   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
897
898  out:
899   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
900   silc_client_command_reply_free(cmd);
901 }
902
903 SILC_CLIENT_CMD_REPLY_FUNC(umode)
904 {
905 }
906
907 /* Received reply for CMODE command. */
908
909 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
910 {
911   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
912   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
913   SilcCommandStatus status;
914   unsigned char *tmp;
915
916   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
917   if (status != SILC_STATUS_OK) {
918     cmd->client->ops->say(cmd->client, conn,
919              "%s", silc_client_command_status_message(status));
920     COMMAND_REPLY_ERROR;
921     goto out;
922   }
923
924   /* Get channel mode */
925   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
926   if (!tmp) {
927     COMMAND_REPLY_ERROR;
928     goto out;
929   }
930
931   /* Notify application */
932   COMMAND_REPLY((ARGS, tmp));
933
934   /* Execute any pending command callbacks */
935   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
936
937  out:
938   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
939   silc_client_command_reply_free(cmd);
940 }
941
942 /* Received reply for CUMODE command */
943
944 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
945 {
946   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
947   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
948   SilcCommandStatus status;
949   SilcIDCacheEntry id_cache = NULL;
950   SilcClientID *client_id;
951   unsigned char *tmp, *id;
952   unsigned int len;
953   
954   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
955   if (status != SILC_STATUS_OK) {
956     cmd->client->ops->say(cmd->client, conn,
957              "%s", silc_client_command_status_message(status));
958     COMMAND_REPLY_ERROR;
959     goto out;
960   }
961   
962   /* Get channel mode */
963   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
964   if (!tmp) {
965     COMMAND_REPLY_ERROR;
966     goto out;
967   }
968
969   /* Get Client ID */
970   id = silc_argument_get_arg_type(cmd->args, 3, &len);
971   if (!id) {
972     COMMAND_REPLY_ERROR;
973     goto out;
974   }
975   client_id = silc_id_payload_parse_id(id, len);
976   if (!client_id) {
977     COMMAND_REPLY_ERROR;
978     goto out;
979   }
980   
981   /* Get client entry */
982   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
983                                    SILC_ID_CLIENT, &id_cache)) {
984     COMMAND_REPLY_ERROR;
985     goto out;
986   }
987
988   /* Notify application */
989   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
990   silc_free(client_id);
991   
992   /* Execute any pending command callbacks */
993   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
994
995  out:
996   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
997   silc_client_command_reply_free(cmd);
998 }
999
1000 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1001 {
1002   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1003   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1004   SilcCommandStatus status;
1005   unsigned char *tmp;
1006
1007   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1008   SILC_GET16_MSB(status, tmp);
1009   if (status != SILC_STATUS_OK) {
1010     cmd->client->ops->say(cmd->client, conn,
1011              "%s", silc_client_command_status_message(status));
1012     COMMAND_REPLY_ERROR;
1013     goto out;
1014   }
1015
1016   /* Notify application */
1017   COMMAND_REPLY((ARGS));
1018
1019   /* Execute any pending command callbacks */
1020   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1021
1022  out:
1023   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1024   silc_client_command_reply_free(cmd);
1025 }
1026
1027 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1028 {
1029 }
1030
1031 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1032 {
1033 }
1034
1035 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1036 {
1037   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1038   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1039   SilcCommandStatus status;
1040   unsigned char *tmp;
1041
1042   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1043   SILC_GET16_MSB(status, tmp);
1044   if (status != SILC_STATUS_OK) {
1045     cmd->client->ops->say(cmd->client, conn,
1046              "%s", silc_client_command_status_message(status));
1047     COMMAND_REPLY_ERROR;
1048     goto out;
1049   }
1050
1051   /* Notify application */
1052   COMMAND_REPLY((ARGS));
1053
1054   /* Execute any pending command callbacks */
1055   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1056
1057  out:
1058   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1059   silc_client_command_reply_free(cmd);
1060 }
1061
1062 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1063 {
1064   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1065   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1066   SilcCommandStatus status;
1067   unsigned char *tmp;
1068
1069   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1070   SILC_GET16_MSB(status, tmp);
1071   if (status != SILC_STATUS_OK) {
1072     cmd->client->ops->say(cmd->client, conn,
1073              "%s", silc_client_command_status_message(status));
1074     COMMAND_REPLY_ERROR;
1075     goto out;
1076   }
1077
1078   /* Notify application */
1079   COMMAND_REPLY((ARGS));
1080
1081   /* Execute any pending command callbacks */
1082   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1083
1084  out:
1085   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1086   silc_client_command_reply_free(cmd);
1087 }
1088  
1089 SILC_CLIENT_CMD_REPLY_FUNC(close)
1090 {
1091   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1092   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1093   SilcCommandStatus status;
1094   unsigned char *tmp;
1095
1096   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1097   SILC_GET16_MSB(status, tmp);
1098   if (status != SILC_STATUS_OK) {
1099     cmd->client->ops->say(cmd->client, conn,
1100              "%s", silc_client_command_status_message(status));
1101     COMMAND_REPLY_ERROR;
1102     goto out;
1103   }
1104
1105   /* Notify application */
1106   COMMAND_REPLY((ARGS));
1107
1108   /* Execute any pending command callbacks */
1109   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1110
1111  out:
1112   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1113   silc_client_command_reply_free(cmd);
1114 }
1115  
1116 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1117 {
1118   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1119   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1120   SilcCommandStatus status;
1121   unsigned char *tmp;
1122
1123   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1124   SILC_GET16_MSB(status, tmp);
1125   if (status != SILC_STATUS_OK) {
1126     cmd->client->ops->say(cmd->client, conn,
1127              "%s", silc_client_command_status_message(status));
1128     COMMAND_REPLY_ERROR;
1129     goto out;
1130   }
1131
1132   /* Notify application */
1133   COMMAND_REPLY((ARGS));
1134
1135   /* Execute any pending command callbacks */
1136   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1137
1138  out:
1139   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1140   silc_client_command_reply_free(cmd);
1141 }
1142  
1143 /* Reply to LEAVE command. */
1144
1145 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1146 {
1147   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1148   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1149   SilcCommandStatus status;
1150   unsigned char *tmp;
1151
1152   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1153   SILC_GET16_MSB(status, tmp);
1154   if (status != SILC_STATUS_OK) {
1155     cmd->client->ops->say(cmd->client, conn,
1156              "%s", silc_client_command_status_message(status));
1157     COMMAND_REPLY_ERROR;
1158     goto out;
1159   }
1160
1161   /* Notify application */
1162   COMMAND_REPLY((ARGS));
1163
1164   /* Execute any pending command callbacks */
1165   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1166
1167  out:
1168   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1169   silc_client_command_reply_free(cmd);
1170 }
1171
1172 /* Reply to USERS command. Received list of client ID's and theirs modes
1173    on the channel we requested. */
1174
1175 SILC_CLIENT_CMD_REPLY_FUNC(users)
1176 {
1177   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1178   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1179   SilcCommandStatus status;
1180   SilcIDCacheEntry id_cache = NULL;
1181   SilcChannelEntry channel;
1182   SilcChannelUser chu;
1183   SilcChannelID *channel_id = NULL;
1184   SilcBuffer client_id_list;
1185   SilcBuffer client_mode_list;
1186   unsigned char *tmp;
1187   unsigned int tmp_len, list_count;
1188   int i;
1189   unsigned char **res_argv = NULL;
1190   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1191
1192   SILC_LOG_DEBUG(("Start"));
1193
1194   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1195   SILC_GET16_MSB(status, tmp);
1196   if (status != SILC_STATUS_OK) {
1197     cmd->client->ops->say(cmd->client, conn,
1198              "%s", silc_client_command_status_message(status));
1199     COMMAND_REPLY_ERROR;
1200     goto out;
1201   }
1202
1203   /* Get channel ID */
1204   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1205   if (!tmp)
1206     goto out;
1207   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1208   if (!channel_id)
1209     goto out;
1210
1211   /* Get the list count */
1212   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1213   if (!tmp)
1214     goto out;
1215   SILC_GET32_MSB(list_count, tmp);
1216
1217   /* Get Client ID list */
1218   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1219   if (!tmp)
1220     goto out;
1221
1222   client_id_list = silc_buffer_alloc(tmp_len);
1223   silc_buffer_pull_tail(client_id_list, tmp_len);
1224   silc_buffer_put(client_id_list, tmp, tmp_len);
1225
1226   /* Get client mode list */
1227   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1228   if (!tmp)
1229     goto out;
1230
1231   client_mode_list = silc_buffer_alloc(tmp_len);
1232   silc_buffer_pull_tail(client_mode_list, tmp_len);
1233   silc_buffer_put(client_mode_list, tmp, tmp_len);
1234
1235   /* Get channel entry */
1236   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1237                                    SILC_ID_CHANNEL, &id_cache)) {
1238     COMMAND_REPLY_ERROR;
1239     goto out;
1240   }
1241   channel = (SilcChannelEntry)id_cache->context;
1242
1243   /* Remove old client list from channel. */
1244   silc_list_start(channel->clients);
1245   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1246     silc_list_del(channel->clients, chu);
1247     silc_free(chu);
1248   }
1249
1250   /* Cache the received Client ID's and modes. This cache expires
1251      whenever server sends notify message to channel. It means two things;
1252      some user has joined or leaved the channel. XXX! */
1253   for (i = 0; i < list_count; i++) {
1254     unsigned short idp_len;
1255     unsigned int mode;
1256     SilcClientID *client_id;
1257     SilcClientEntry client;
1258
1259     /* Client ID */
1260     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1261     idp_len += 4;
1262     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1263     if (!client_id)
1264       continue;
1265
1266     /* Mode */
1267     SILC_GET32_MSB(mode, client_mode_list->data);
1268
1269     /* Check if we have this client cached already. */
1270     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1271                                      SILC_ID_CLIENT, &id_cache)) {
1272       /* No we don't have it, query it from the server. Assemble argument
1273          table that will be sent fr the IDENTIFY command later. */
1274       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1275                               (res_argc + 1));
1276       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1277                                    (res_argc + 1));
1278       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1279                                     (res_argc + 1));
1280       res_argv[res_argc] = client_id_list->data;
1281       res_argv_lens[res_argc] = idp_len;
1282       res_argv_types[res_argc] = res_argc + 3;
1283       res_argc++;
1284     } else {
1285       /* Found the client, join it to the channel */
1286       client = (SilcClientEntry)id_cache->context;
1287       chu = silc_calloc(1, sizeof(*chu));
1288       chu->client = client;
1289       chu->mode = mode;
1290       silc_list_add(channel->clients, chu);
1291
1292       silc_free(client_id);
1293       id_cache = NULL;
1294     }
1295
1296     silc_buffer_pull(client_id_list, idp_len);
1297     silc_buffer_pull(client_mode_list, 4);
1298   }
1299
1300   /* Query the client information from server if the list included clients
1301      that we don't know about. */
1302   if (res_argc) {
1303     SilcBuffer res_cmd;
1304
1305     /* Send the IDENTIFY command to server */
1306     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1307                                           res_argc, res_argv, res_argv_lens,
1308                                           res_argv_types, ++conn->cmd_ident);
1309     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1310                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1311                             TRUE);
1312
1313     /* Register pending command callback. After we've received the IDENTIFY
1314        command reply we will reprocess this command reply by re-calling this
1315        USERS command reply callback. */
1316     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1317                                 NULL, silc_client_command_reply_users, cmd);
1318
1319     silc_buffer_free(res_cmd);
1320     if (channel_id)
1321       silc_free(channel_id);
1322
1323     silc_free(res_argv);
1324     silc_free(res_argv_lens);
1325     silc_free(res_argv_types);
1326     return;
1327   }
1328
1329   /* We have all the clients on the channel cached now. Create a nice
1330      output for user interface and notify application. */
1331
1332   if (!cmd->callback) {
1333     /* Server has sent us USERS reply even when we haven't actually sent
1334        USERS command. This is normal behaviour when joining to a channel.
1335        Display some nice information on the user interface. */
1336     int k = 0, len1 = 0, len2 = 0;
1337     char *name_list = NULL;
1338
1339     silc_list_start(channel->clients);
1340     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1341       char *m, *n = chu->client->nickname;
1342       len2 = strlen(n);
1343       len1 += len2;
1344
1345       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1346
1347       m = silc_client_chumode_char(chu->mode);
1348       if (m) {
1349         memcpy(name_list + (len1 - len2), m, strlen(m));
1350         len1 += strlen(m);
1351         silc_free(m);
1352       }
1353
1354       memcpy(name_list + (len1 - len2), n, len2);
1355       name_list[len1] = 0;
1356       
1357       if (k == silc_list_count(channel->clients) - 1)
1358         break;
1359       memcpy(name_list + len1, " ", 1);
1360       len1++;
1361       k++;
1362     }
1363
1364     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1365                           channel->channel_name, name_list);
1366     silc_free(name_list);
1367   }
1368
1369   /* Notify application */
1370   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1371                  client_mode_list->head));
1372
1373   /* Execute any pending command callbacks */
1374   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1375
1376   silc_buffer_free(client_id_list);
1377   silc_buffer_free(client_mode_list);
1378
1379  out:
1380   if (channel_id)
1381     silc_free(channel_id);
1382   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1383   silc_client_command_reply_free(cmd);
1384 }