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