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       status != SILC_STATUS_LIST_START &&
391       status != SILC_STATUS_LIST_ITEM &&
392       status != SILC_STATUS_LIST_END) {
393     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
394       /* Take nickname which may be provided */
395       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
396       if (tmp)
397         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
398                  silc_client_command_status_message(status));
399       else
400         cmd->client->ops->say(cmd->client, conn, "%s",
401                  silc_client_command_status_message(status));
402       COMMAND_REPLY_ERROR;
403       goto out;
404     } else {
405       cmd->client->ops->say(cmd->client, conn,
406                "%s", silc_client_command_status_message(status));
407       COMMAND_REPLY_ERROR;
408       goto out;
409     }
410   }
411
412   /* Display one whois reply */
413   if (status == SILC_STATUS_OK ||
414       status == SILC_STATUS_LIST_START ||
415       status == SILC_STATUS_LIST_ITEM ||
416       status == SILC_STATUS_LIST_END) {
417     unsigned int len;
418     unsigned char *id_data;
419     char *nickname;
420     char *username;
421     SilcClientID *client_id;
422
423     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
424     if (!id_data)
425       goto out;
426     client_id = silc_id_payload_parse_id(id_data, len);
427     if (!client_id)
428       goto out;
429
430     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
431     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
432
433     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
434                                      SILC_ID_CLIENT, &id_cache)) {
435       client_entry = silc_calloc(1, sizeof(*client_entry));
436       client_entry->id = client_id;
437       silc_parse_nickname(nickname, &client_entry->nickname, 
438                           &client_entry->server, &client_entry->num);
439       if (username)
440         client_entry->username = strdup(username);
441     
442       /* Add client to cache */
443       silc_idcache_add(conn->client_cache, client_entry->nickname,
444                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
445     } else {
446       client_entry = (SilcClientEntry)id_cache->context;
447       if (client_entry->nickname)
448         silc_free(client_entry->nickname);
449       if (client_entry->server)
450         silc_free(client_entry->server);
451       if (username && client_entry->username)
452         silc_free(client_entry->username);
453
454       silc_parse_nickname(nickname, &client_entry->nickname, 
455                           &client_entry->server, &client_entry->num);
456
457       if (username)
458         client_entry->username = strdup(username);
459
460       id_cache->data = client_entry->nickname;
461       silc_idcache_sort_by_data(conn->client_cache);
462     
463       silc_free(client_id);
464     }
465   }
466
467   if (status != SILC_STATUS_OK &&
468       status != SILC_STATUS_LIST_END) {
469     silc_client_command_reply_free(cmd);
470     return;
471   }
472
473   /* Execute any pending command callbacks */
474   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
475
476  out:
477   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
478   silc_client_command_reply_free(cmd);
479 }
480
481 /* Received reply for command NICK. If everything went without errors
482    we just received our new Client ID. */
483
484 SILC_CLIENT_CMD_REPLY_FUNC(nick)
485 {
486   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
487   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
488   SilcCommandStatus status;
489   SilcIDPayload idp;
490   unsigned char *tmp;
491   unsigned int argc, len;
492
493   SILC_LOG_DEBUG(("Start"));
494
495   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
496   if (status != SILC_STATUS_OK) {
497     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
498              silc_client_command_status_message(status));
499     COMMAND_REPLY_ERROR;
500     goto out;
501   }
502
503   argc = silc_argument_get_arg_num(cmd->args);
504   if (argc < 2 || argc > 2) {
505     cmd->client->ops->say(cmd->client, conn, 
506                           "Cannot set nickname: bad reply to command");
507     COMMAND_REPLY_ERROR;
508     goto out;
509   }
510
511   /* Take received Client ID */
512   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
513   idp = silc_id_payload_parse_data(tmp, len);
514   if (!idp) {
515     COMMAND_REPLY_ERROR;
516     goto out;
517   }
518   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
519     
520   /* Notify application */
521   COMMAND_REPLY((ARGS, conn->local_entry));
522
523   /* Execute any pending command callbacks */
524   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
525
526  out:
527   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
528   silc_client_command_reply_free(cmd);
529 }
530
531 SILC_CLIENT_CMD_REPLY_FUNC(list)
532 {
533 }
534
535 /* Received reply to topic command. */
536
537 SILC_CLIENT_CMD_REPLY_FUNC(topic)
538 {
539   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
540   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
541   SilcCommandStatus status;
542   SilcChannelEntry channel;
543   SilcChannelID *channel_id = NULL;
544   SilcIDCacheEntry id_cache = NULL;
545   unsigned char *tmp;
546   char *topic;
547   unsigned int argc, len;
548
549   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
550   if (status != SILC_STATUS_OK) {
551     cmd->client->ops->say(cmd->client, conn,
552              "%s", silc_client_command_status_message(status));
553     COMMAND_REPLY_ERROR;
554     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
555     silc_client_command_reply_free(cmd);
556     return;
557   }
558
559   argc = silc_argument_get_arg_num(cmd->args);
560   if (argc < 1 || argc > 3) {
561     COMMAND_REPLY_ERROR;
562     goto out;
563   }
564
565   /* Take Channel ID */
566   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
567   if (!tmp)
568     goto out;
569
570   /* Take topic */
571   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
572   if (!topic)
573     goto out;
574
575   channel_id = silc_id_payload_parse_id(tmp, len);
576   if (!channel_id)
577     goto out;
578
579   /* Get the channel name */
580   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
581                                    SILC_ID_CHANNEL, &id_cache)) {
582     silc_free(channel_id);
583     COMMAND_REPLY_ERROR;
584     goto out;
585   }
586   
587   channel = (SilcChannelEntry)id_cache->context;
588
589   cmd->client->ops->say(cmd->client, conn, 
590                         "Topic on channel %s: %s", channel->channel_name,
591                         topic);
592
593   /* Notify application */
594   COMMAND_REPLY((ARGS, channel, topic));
595
596   /* Execute any pending command callbacks */
597   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
598
599  out:
600   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
601   silc_client_command_reply_free(cmd);
602 }
603
604 /* Received reply to invite command. */
605
606 SILC_CLIENT_CMD_REPLY_FUNC(invite)
607 {
608   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
609   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
610   SilcCommandStatus status;
611   unsigned char *tmp;
612
613   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
614   SILC_GET16_MSB(status, tmp);
615   if (status != SILC_STATUS_OK) {
616     cmd->client->ops->say(cmd->client, conn,
617              "%s", silc_client_command_status_message(status));
618     COMMAND_REPLY_ERROR;
619     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
620     silc_client_command_reply_free(cmd);
621     return;
622   }
623
624   /* Notify application */
625   COMMAND_REPLY((ARGS));
626
627   /* Execute any pending command callbacks */
628   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
629
630   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
631   silc_client_command_reply_free(cmd);
632 }
633  
634 SILC_CLIENT_CMD_REPLY_FUNC(kill)
635 {
636 }
637
638 /* Received reply to INFO command. We receive the server ID and some
639    information about the server user requested. */
640
641 SILC_CLIENT_CMD_REPLY_FUNC(info)
642 {
643   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
644   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
645   SilcClient client = cmd->client;
646   SilcCommandStatus status;
647   unsigned char *tmp;
648
649   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
650   SILC_GET16_MSB(status, tmp);
651   if (status != SILC_STATUS_OK) {
652     cmd->client->ops->say(cmd->client, conn,
653              "%s", silc_client_command_status_message(status));
654     COMMAND_REPLY_ERROR;
655     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
656     silc_client_command_reply_free(cmd);
657     return;
658   }
659
660   /* Get server ID */
661   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
662   if (!tmp)
663     goto out;
664
665   /* XXX save server id */
666
667   /* Get server info */
668   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
669   if (!tmp)
670     goto out;
671
672   client->ops->say(cmd->client, conn, "Info: %s", tmp);
673
674   /* Notify application */
675   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
676
677   /* Execute any pending command callbacks */
678   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
679
680  out:
681   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
682   silc_client_command_reply_free(cmd);
683 }
684
685 /* Received reply to PING command. The reply time is shown to user. */
686
687 SILC_CLIENT_CMD_REPLY_FUNC(ping)
688 {
689   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
690   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
691   SilcCommandStatus status;
692   void *id;
693   int i;
694   time_t diff, curtime;
695
696   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
697   if (status != SILC_STATUS_OK) {
698     cmd->client->ops->say(cmd->client, conn,
699              "%s", silc_client_command_status_message(status));
700     COMMAND_REPLY_ERROR;
701     goto out;
702   }
703
704   curtime = time(NULL);
705   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
706                       cmd->packet->src_id_type);
707   if (!id) {
708     COMMAND_REPLY_ERROR;
709     goto out;
710   }
711
712   for (i = 0; i < conn->ping_count; i++) {
713     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
714       diff = curtime - conn->ping[i].start_time;
715       cmd->client->ops->say(cmd->client, conn, 
716                             "Ping reply from %s: %d second%s", 
717                             conn->ping[i].dest_name, diff, 
718                             diff == 1 ? "" : "s");
719       
720       conn->ping[i].start_time = 0;
721       silc_free(conn->ping[i].dest_id);
722       conn->ping[i].dest_id = NULL;
723       silc_free(conn->ping[i].dest_name);
724       conn->ping[i].dest_name = NULL;
725       break;
726     }
727   }
728
729   silc_free(id);
730
731   /* Notify application */
732   COMMAND_REPLY((ARGS));
733
734   /* Execute any pending command callbacks */
735   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
736
737  out:
738   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
739   silc_client_command_reply_free(cmd);
740 }
741
742 /* Received reply for JOIN command. */
743
744 SILC_CLIENT_CMD_REPLY_FUNC(join)
745 {
746   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
747   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
748   SilcCommandStatus status;
749   SilcIDPayload idp = NULL;
750   SilcChannelEntry channel;
751   SilcIDCacheEntry id_cache = NULL;
752   SilcChannelUser chu;
753   unsigned int argc, mode, len, list_count;
754   char *topic, *tmp, *channel_name = NULL, *hmac;
755   SilcBuffer keyp, client_id_list, client_mode_list;
756   int i;
757
758   SILC_LOG_DEBUG(("Start"));
759
760   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
761   if (status != SILC_STATUS_OK) {
762     cmd->client->ops->say(cmd->client, conn,
763              "%s", silc_client_command_status_message(status));
764     COMMAND_REPLY_ERROR;
765     goto out;
766   }
767
768   argc = silc_argument_get_arg_num(cmd->args);
769   if (argc < 7 || argc > 14) {
770     cmd->client->ops->say(cmd->client, conn,
771              "Cannot join channel: Bad reply packet");
772     COMMAND_REPLY_ERROR;
773     goto out;
774   }
775
776   /* Get channel name */
777   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
778   if (!tmp) {
779     cmd->client->ops->say(cmd->client, conn, 
780                           "Cannot join channel: Bad reply packet");
781     COMMAND_REPLY_ERROR;
782     goto out;
783   }
784   channel_name = strdup(tmp);
785
786   /* Get Channel ID */
787   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
788   if (!tmp) {
789     cmd->client->ops->say(cmd->client, conn, 
790                           "Cannot join channel: Bad reply packet");
791     COMMAND_REPLY_ERROR;
792     silc_free(channel_name);
793     goto out;
794   }
795   idp = silc_id_payload_parse_data(tmp, len);
796   if (!idp) {
797     COMMAND_REPLY_ERROR;
798     silc_free(channel_name);
799     goto out;
800   }
801
802   /* Get channel mode */
803   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
804   if (tmp)
805     SILC_GET32_MSB(mode, tmp);
806   else
807     mode = 0;
808
809   /* Get channel key */
810   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
811   if (!tmp) {
812     silc_id_payload_free(idp);
813     silc_free(channel_name);
814     goto out;
815   }
816   keyp = silc_buffer_alloc(len);
817   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
818   silc_buffer_put(keyp, tmp, len);
819
820   /* Get topic */
821   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
822
823   /* Save received Channel ID. This actually creates the channel */
824   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
825                                        mode, idp);
826   silc_id_payload_free(idp);
827
828   /* Get hmac */
829   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
830   if (hmac) {
831     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
832       cmd->client->ops->say(cmd->client, conn, 
833                             "Cannot join channel: Unsupported HMAC `%s'",
834                             hmac);
835       COMMAND_REPLY_ERROR;
836       silc_free(channel_name);
837       goto out;
838     }
839   }
840
841   /* Get the list count */
842   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
843   if (!tmp)
844     goto out;
845   SILC_GET32_MSB(list_count, tmp);
846
847   /* Get Client ID list */
848   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
849   if (!tmp)
850     goto out;
851
852   client_id_list = silc_buffer_alloc(len);
853   silc_buffer_pull_tail(client_id_list, len);
854   silc_buffer_put(client_id_list, tmp, len);
855
856   /* Get client mode list */
857   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
858   if (!tmp)
859     goto out;
860
861   client_mode_list = silc_buffer_alloc(len);
862   silc_buffer_pull_tail(client_mode_list, len);
863   silc_buffer_put(client_mode_list, tmp, len);
864
865   /* Add clients we received in the reply to the channel */
866   for (i = 0; i < list_count; i++) {
867     unsigned short idp_len;
868     unsigned int mode;
869     SilcClientID *client_id;
870     SilcClientEntry client_entry;
871
872     /* Client ID */
873     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
874     idp_len += 4;
875     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
876     if (!client_id)
877       continue;
878
879     /* Mode */
880     SILC_GET32_MSB(mode, client_mode_list->data);
881
882     /* Check if we have this client cached already. */
883     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
884                                      SILC_ID_CLIENT, &id_cache)) {
885       /* No, we don't have it, add entry for it. */
886       client_entry = silc_calloc(1, sizeof(*client_entry));
887       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
888       silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
889                        client_entry->id, (void *)client_entry, FALSE);
890     } else {
891       /* Yes, we have it already */
892       client_entry = (SilcClientEntry)id_cache->context;
893     }
894
895     /* Join the client to the channel */
896     chu = silc_calloc(1, sizeof(*chu));
897     chu->client = client_entry;
898     chu->mode = mode;
899     silc_list_add(channel->clients, chu);
900     silc_free(client_id);
901
902     silc_buffer_pull(client_id_list, idp_len);
903     silc_buffer_pull(client_mode_list, 4);
904   }
905   silc_buffer_push(client_id_list, client_id_list->data - 
906                    client_id_list->head);
907   silc_buffer_push(client_mode_list, client_mode_list->data - 
908                    client_mode_list->head);
909
910   /* Save channel key */
911   silc_client_save_channel_key(conn, keyp, channel);
912
913   /* Notify application */
914   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
915                  NULL, topic, hmac, list_count, client_id_list, 
916                  client_mode_list));
917
918   /* Execute any pending command callbacks */
919   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
920
921   silc_buffer_free(keyp);
922   silc_buffer_free(client_id_list);
923   silc_buffer_free(client_mode_list);
924
925  out:
926   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
927   silc_client_command_reply_free(cmd);
928 }
929
930 /* Received reply for MOTD command */
931
932 SILC_CLIENT_CMD_REPLY_FUNC(motd)
933 {
934   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
935   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
936   SilcCommandStatus status;
937   unsigned int argc, i;
938   unsigned char *tmp;
939   char *motd = NULL, *cp, line[256];
940
941   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
942   SILC_GET16_MSB(status, tmp);
943   if (status != SILC_STATUS_OK) {
944     cmd->client->ops->say(cmd->client, conn,
945              "%s", silc_client_command_status_message(status));
946     COMMAND_REPLY_ERROR;
947     return;
948   }
949
950   argc = silc_argument_get_arg_num(cmd->args);
951   if (argc > 2) {
952     COMMAND_REPLY_ERROR;
953     goto out;
954   }
955
956   if (argc == 2) {
957     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
958     if (!motd) {
959       COMMAND_REPLY_ERROR;
960       goto out;
961     }
962
963     i = 0;
964     cp = motd;
965     while(cp[i] != 0) {
966       if (cp[i++] == '\n') {
967         memset(line, 0, sizeof(line));
968         strncat(line, cp, i - 1);
969         cp += i;
970         
971         if (i == 2)
972           line[0] = ' ';
973         
974         cmd->client->ops->say(cmd->client, conn, "%s", line);
975         
976         if (!strlen(cp))
977           break;
978         i = 0;
979       }
980     }
981   }
982
983   /* Notify application */
984   COMMAND_REPLY((ARGS, motd));
985
986   /* Execute any pending command callbacks */
987   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
988
989  out:
990   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
991   silc_client_command_reply_free(cmd);
992 }
993
994 SILC_CLIENT_CMD_REPLY_FUNC(umode)
995 {
996 }
997
998 /* Received reply for CMODE command. */
999
1000 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1001 {
1002   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1003   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1004   SilcCommandStatus status;
1005   unsigned char *tmp;
1006
1007   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1008   if (status != SILC_STATUS_OK) {
1009     cmd->client->ops->say(cmd->client, conn,
1010              "%s", silc_client_command_status_message(status));
1011     COMMAND_REPLY_ERROR;
1012     goto out;
1013   }
1014
1015   /* Get channel mode */
1016   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1017   if (!tmp) {
1018     COMMAND_REPLY_ERROR;
1019     goto out;
1020   }
1021
1022   /* Notify application */
1023   COMMAND_REPLY((ARGS, tmp));
1024
1025   /* Execute any pending command callbacks */
1026   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1027
1028  out:
1029   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1030   silc_client_command_reply_free(cmd);
1031 }
1032
1033 /* Received reply for CUMODE command */
1034
1035 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1036 {
1037   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1038   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1039   SilcCommandStatus status;
1040   SilcIDCacheEntry id_cache = NULL;
1041   SilcClientID *client_id;
1042   unsigned char *tmp, *id;
1043   unsigned int len;
1044   
1045   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1046   if (status != SILC_STATUS_OK) {
1047     cmd->client->ops->say(cmd->client, conn,
1048              "%s", silc_client_command_status_message(status));
1049     COMMAND_REPLY_ERROR;
1050     goto out;
1051   }
1052   
1053   /* Get channel mode */
1054   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1055   if (!tmp) {
1056     COMMAND_REPLY_ERROR;
1057     goto out;
1058   }
1059
1060   /* Get Client ID */
1061   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1062   if (!id) {
1063     COMMAND_REPLY_ERROR;
1064     goto out;
1065   }
1066   client_id = silc_id_payload_parse_id(id, len);
1067   if (!client_id) {
1068     COMMAND_REPLY_ERROR;
1069     goto out;
1070   }
1071   
1072   /* Get client entry */
1073   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1074                                    SILC_ID_CLIENT, &id_cache)) {
1075     COMMAND_REPLY_ERROR;
1076     goto out;
1077   }
1078
1079   /* Notify application */
1080   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1081   silc_free(client_id);
1082   
1083   /* Execute any pending command callbacks */
1084   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1085
1086  out:
1087   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1088   silc_client_command_reply_free(cmd);
1089 }
1090
1091 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1092 {
1093   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1094   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1095   SilcCommandStatus status;
1096   unsigned char *tmp;
1097
1098   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1099   SILC_GET16_MSB(status, tmp);
1100   if (status != SILC_STATUS_OK) {
1101     cmd->client->ops->say(cmd->client, conn,
1102              "%s", silc_client_command_status_message(status));
1103     COMMAND_REPLY_ERROR;
1104     goto out;
1105   }
1106
1107   /* Notify application */
1108   COMMAND_REPLY((ARGS));
1109
1110   /* Execute any pending command callbacks */
1111   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1112
1113  out:
1114   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1115   silc_client_command_reply_free(cmd);
1116 }
1117
1118 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1119 {
1120 }
1121
1122 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1123 {
1124 }
1125
1126 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1127 {
1128   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1129   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1130   SilcCommandStatus status;
1131   unsigned char *tmp;
1132
1133   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1134   SILC_GET16_MSB(status, tmp);
1135   if (status != SILC_STATUS_OK) {
1136     cmd->client->ops->say(cmd->client, conn,
1137              "%s", silc_client_command_status_message(status));
1138     COMMAND_REPLY_ERROR;
1139     goto out;
1140   }
1141
1142   /* Notify application */
1143   COMMAND_REPLY((ARGS));
1144
1145   /* Execute any pending command callbacks */
1146   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1147
1148  out:
1149   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1150   silc_client_command_reply_free(cmd);
1151 }
1152
1153 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1154 {
1155   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1156   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1157   SilcCommandStatus status;
1158   unsigned char *tmp;
1159
1160   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1161   SILC_GET16_MSB(status, tmp);
1162   if (status != SILC_STATUS_OK) {
1163     cmd->client->ops->say(cmd->client, conn,
1164              "%s", silc_client_command_status_message(status));
1165     COMMAND_REPLY_ERROR;
1166     goto out;
1167   }
1168
1169   /* Notify application */
1170   COMMAND_REPLY((ARGS));
1171
1172   /* Execute any pending command callbacks */
1173   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1174
1175  out:
1176   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1177   silc_client_command_reply_free(cmd);
1178 }
1179  
1180 SILC_CLIENT_CMD_REPLY_FUNC(close)
1181 {
1182   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1183   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1184   SilcCommandStatus status;
1185   unsigned char *tmp;
1186
1187   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1188   SILC_GET16_MSB(status, tmp);
1189   if (status != SILC_STATUS_OK) {
1190     cmd->client->ops->say(cmd->client, conn,
1191              "%s", silc_client_command_status_message(status));
1192     COMMAND_REPLY_ERROR;
1193     goto out;
1194   }
1195
1196   /* Notify application */
1197   COMMAND_REPLY((ARGS));
1198
1199   /* Execute any pending command callbacks */
1200   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1201
1202  out:
1203   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1204   silc_client_command_reply_free(cmd);
1205 }
1206  
1207 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1208 {
1209   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1210   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1211   SilcCommandStatus status;
1212   unsigned char *tmp;
1213
1214   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1215   SILC_GET16_MSB(status, tmp);
1216   if (status != SILC_STATUS_OK) {
1217     cmd->client->ops->say(cmd->client, conn,
1218              "%s", silc_client_command_status_message(status));
1219     COMMAND_REPLY_ERROR;
1220     goto out;
1221   }
1222
1223   /* Notify application */
1224   COMMAND_REPLY((ARGS));
1225
1226   /* Execute any pending command callbacks */
1227   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1228
1229  out:
1230   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1231   silc_client_command_reply_free(cmd);
1232 }
1233  
1234 /* Reply to LEAVE command. */
1235
1236 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1237 {
1238   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1239   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1240   SilcCommandStatus status;
1241   unsigned char *tmp;
1242
1243   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1244   SILC_GET16_MSB(status, tmp);
1245   if (status != SILC_STATUS_OK) {
1246     cmd->client->ops->say(cmd->client, conn,
1247              "%s", silc_client_command_status_message(status));
1248     COMMAND_REPLY_ERROR;
1249     goto out;
1250   }
1251
1252   /* Notify application */
1253   COMMAND_REPLY((ARGS));
1254
1255   /* Execute any pending command callbacks */
1256   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1257
1258  out:
1259   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1260   silc_client_command_reply_free(cmd);
1261 }
1262
1263 /* Reply to USERS command. Received list of client ID's and theirs modes
1264    on the channel we requested. */
1265
1266 SILC_CLIENT_CMD_REPLY_FUNC(users)
1267 {
1268   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1269   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1270   SilcCommandStatus status;
1271   SilcIDCacheEntry id_cache = NULL;
1272   SilcChannelEntry channel;
1273   SilcChannelUser chu;
1274   SilcChannelID *channel_id = NULL;
1275   SilcBuffer client_id_list;
1276   SilcBuffer client_mode_list;
1277   unsigned char *tmp;
1278   unsigned int tmp_len, list_count;
1279   int i;
1280   unsigned char **res_argv = NULL;
1281   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1282
1283   SILC_LOG_DEBUG(("Start"));
1284
1285   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1286   SILC_GET16_MSB(status, tmp);
1287   if (status != SILC_STATUS_OK) {
1288     cmd->client->ops->say(cmd->client, conn,
1289              "%s", silc_client_command_status_message(status));
1290     COMMAND_REPLY_ERROR;
1291     goto out;
1292   }
1293
1294   /* Get channel ID */
1295   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1296   if (!tmp)
1297     goto out;
1298   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1299   if (!channel_id)
1300     goto out;
1301
1302   /* Get the list count */
1303   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1304   if (!tmp)
1305     goto out;
1306   SILC_GET32_MSB(list_count, tmp);
1307
1308   /* Get Client ID list */
1309   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1310   if (!tmp)
1311     goto out;
1312
1313   client_id_list = silc_buffer_alloc(tmp_len);
1314   silc_buffer_pull_tail(client_id_list, tmp_len);
1315   silc_buffer_put(client_id_list, tmp, tmp_len);
1316
1317   /* Get client mode list */
1318   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1319   if (!tmp)
1320     goto out;
1321
1322   client_mode_list = silc_buffer_alloc(tmp_len);
1323   silc_buffer_pull_tail(client_mode_list, tmp_len);
1324   silc_buffer_put(client_mode_list, tmp, tmp_len);
1325
1326   /* Get channel entry */
1327   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1328                                    SILC_ID_CHANNEL, &id_cache)) {
1329     COMMAND_REPLY_ERROR;
1330     goto out;
1331   }
1332   channel = (SilcChannelEntry)id_cache->context;
1333
1334   /* Remove old client list from channel. */
1335   silc_list_start(channel->clients);
1336   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1337     silc_list_del(channel->clients, chu);
1338     silc_free(chu);
1339   }
1340
1341   /* Cache the received Client ID's and modes. This cache expires
1342      whenever server sends notify message to channel. It means two things;
1343      some user has joined or leaved the channel. XXX! */
1344   for (i = 0; i < list_count; i++) {
1345     unsigned short idp_len;
1346     unsigned int mode;
1347     SilcClientID *client_id;
1348     SilcClientEntry client;
1349
1350     /* Client ID */
1351     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1352     idp_len += 4;
1353     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1354     if (!client_id)
1355       continue;
1356
1357     /* Mode */
1358     SILC_GET32_MSB(mode, client_mode_list->data);
1359
1360     /* Check if we have this client cached already. */
1361     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1362                                      SILC_ID_CLIENT, &id_cache)) {
1363       /* No we don't have it, query it from the server. Assemble argument
1364          table that will be sent fr the IDENTIFY command later. */
1365       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1366                               (res_argc + 1));
1367       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1368                                    (res_argc + 1));
1369       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1370                                     (res_argc + 1));
1371       res_argv[res_argc] = client_id_list->data;
1372       res_argv_lens[res_argc] = idp_len;
1373       res_argv_types[res_argc] = res_argc + 3;
1374       res_argc++;
1375     } else {
1376       /* Found the client, join it to the channel */
1377       client = (SilcClientEntry)id_cache->context;
1378       chu = silc_calloc(1, sizeof(*chu));
1379       chu->client = client;
1380       chu->mode = mode;
1381       silc_list_add(channel->clients, chu);
1382
1383       silc_free(client_id);
1384       id_cache = NULL;
1385     }
1386
1387     silc_buffer_pull(client_id_list, idp_len);
1388     silc_buffer_pull(client_mode_list, 4);
1389   }
1390
1391   /* Query the client information from server if the list included clients
1392      that we don't know about. */
1393   if (res_argc) {
1394     SilcBuffer res_cmd;
1395
1396     /* Send the IDENTIFY command to server */
1397     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1398                                           res_argc, res_argv, res_argv_lens,
1399                                           res_argv_types, ++conn->cmd_ident);
1400     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1401                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1402                             TRUE);
1403
1404     /* Register pending command callback. After we've received the IDENTIFY
1405        command reply we will reprocess this command reply by re-calling this
1406        USERS command reply callback. */
1407     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1408                                 NULL, silc_client_command_reply_users, cmd);
1409
1410     silc_buffer_free(res_cmd);
1411     if (channel_id)
1412       silc_free(channel_id);
1413
1414     silc_free(res_argv);
1415     silc_free(res_argv_lens);
1416     silc_free(res_argv_types);
1417     return;
1418   }
1419
1420   /* Notify application */
1421   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1422
1423   /* Execute any pending command callbacks */
1424   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1425
1426   silc_buffer_free(client_id_list);
1427   silc_buffer_free(client_mode_list);
1428
1429  out:
1430   if (channel_id)
1431     silc_free(channel_id);
1432   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1433   silc_client_command_reply_free(cmd);
1434 }