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   SilcCommandStatus status;
745   SilcIDPayload idp = NULL;
746   SilcChannelEntry channel;
747   SilcIDCacheEntry id_cache = NULL;
748   SilcChannelUser chu;
749   unsigned int argc, mode, len, list_count;
750   char *topic, *tmp, *channel_name = NULL, *hmac;
751   SilcBuffer keyp, client_id_list, client_mode_list;
752   int i;
753
754   SILC_LOG_DEBUG(("Start"));
755
756   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
757   if (status != SILC_STATUS_OK) {
758     cmd->client->ops->say(cmd->client, conn,
759              "%s", silc_client_command_status_message(status));
760     COMMAND_REPLY_ERROR;
761     goto out;
762   }
763
764   argc = silc_argument_get_arg_num(cmd->args);
765   if (argc < 7 || argc > 14) {
766     cmd->client->ops->say(cmd->client, conn,
767              "Cannot join channel: Bad reply packet");
768     COMMAND_REPLY_ERROR;
769     goto out;
770   }
771
772   /* Get channel name */
773   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
774   if (!tmp) {
775     cmd->client->ops->say(cmd->client, conn, 
776                           "Cannot join channel: Bad reply packet");
777     COMMAND_REPLY_ERROR;
778     goto out;
779   }
780   channel_name = strdup(tmp);
781
782   /* Get Channel ID */
783   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
784   if (!tmp) {
785     cmd->client->ops->say(cmd->client, conn, 
786                           "Cannot join channel: Bad reply packet");
787     COMMAND_REPLY_ERROR;
788     silc_free(channel_name);
789     goto out;
790   }
791   idp = silc_id_payload_parse_data(tmp, len);
792   if (!idp) {
793     COMMAND_REPLY_ERROR;
794     silc_free(channel_name);
795     goto out;
796   }
797
798   /* Get channel mode */
799   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
800   if (tmp)
801     SILC_GET32_MSB(mode, tmp);
802   else
803     mode = 0;
804
805   /* Get channel key */
806   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
807   if (!tmp) {
808     silc_id_payload_free(idp);
809     silc_free(channel_name);
810     goto out;
811   }
812   keyp = silc_buffer_alloc(len);
813   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
814   silc_buffer_put(keyp, tmp, len);
815
816   /* Get topic */
817   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
818
819   /* Save received Channel ID. This actually creates the channel */
820   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
821                                        mode, idp);
822   silc_id_payload_free(idp);
823
824   /* Get hmac */
825   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
826   if (hmac) {
827     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
828       cmd->client->ops->say(cmd->client, conn, 
829                             "Cannot join channel: Unsupported HMAC `%s'",
830                             hmac);
831       COMMAND_REPLY_ERROR;
832       silc_free(channel_name);
833       goto out;
834     }
835   }
836
837   /* Get the list count */
838   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
839   if (!tmp)
840     goto out;
841   SILC_GET32_MSB(list_count, tmp);
842
843   /* Get Client ID list */
844   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
845   if (!tmp)
846     goto out;
847
848   client_id_list = silc_buffer_alloc(len);
849   silc_buffer_pull_tail(client_id_list, len);
850   silc_buffer_put(client_id_list, tmp, len);
851
852   /* Get client mode list */
853   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
854   if (!tmp)
855     goto out;
856
857   client_mode_list = silc_buffer_alloc(len);
858   silc_buffer_pull_tail(client_mode_list, len);
859   silc_buffer_put(client_mode_list, tmp, len);
860
861   /* Add clients we received in the reply to the channel */
862   for (i = 0; i < list_count; i++) {
863     unsigned short idp_len;
864     unsigned int mode;
865     SilcClientID *client_id;
866     SilcClientEntry client_entry;
867
868     /* Client ID */
869     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
870     idp_len += 4;
871     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
872     if (!client_id)
873       continue;
874
875     /* Mode */
876     SILC_GET32_MSB(mode, client_mode_list->data);
877
878     /* Check if we have this client cached already. */
879     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
880                                      SILC_ID_CLIENT, &id_cache)) {
881       /* No, we don't have it, add entry for it. */
882       client_entry = silc_calloc(1, sizeof(*client_entry));
883       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
884       silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
885                        client_entry->id, (void *)client_entry, FALSE);
886     } else {
887       /* Yes, we have it already */
888       client_entry = (SilcClientEntry)id_cache->context;
889       if (client_entry == conn->local_entry)
890         continue;
891     }
892
893     /* Join the client to the channel */
894     chu = silc_calloc(1, sizeof(*chu));
895     chu->client = client_entry;
896     chu->mode = mode;
897     silc_list_add(channel->clients, chu);
898     silc_free(client_id);
899
900     silc_buffer_pull(client_id_list, idp_len);
901     silc_buffer_pull(client_mode_list, 4);
902   }
903   silc_buffer_push(client_id_list, client_id_list->data - 
904                    client_id_list->head);
905   silc_buffer_push(client_mode_list, client_mode_list->data - 
906                    client_mode_list->head);
907
908   /* Save channel key */
909   silc_client_save_channel_key(conn, keyp, channel);
910
911   /* Notify application */
912   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
913                  NULL, topic, hmac, list_count, client_id_list, 
914                  client_mode_list));
915
916   /* Execute any pending command callbacks */
917   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
918
919   silc_buffer_free(keyp);
920   silc_buffer_free(client_id_list);
921   silc_buffer_free(client_mode_list);
922
923  out:
924   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
925   silc_client_command_reply_free(cmd);
926 }
927
928 /* Received reply for MOTD command */
929
930 SILC_CLIENT_CMD_REPLY_FUNC(motd)
931 {
932   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
933   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
934   SilcCommandStatus status;
935   unsigned int argc, i;
936   unsigned char *tmp;
937   char *motd = NULL, *cp, line[256];
938
939   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
940   SILC_GET16_MSB(status, tmp);
941   if (status != SILC_STATUS_OK) {
942     cmd->client->ops->say(cmd->client, conn,
943              "%s", silc_client_command_status_message(status));
944     COMMAND_REPLY_ERROR;
945     return;
946   }
947
948   argc = silc_argument_get_arg_num(cmd->args);
949   if (argc > 2) {
950     COMMAND_REPLY_ERROR;
951     goto out;
952   }
953
954   if (argc == 2) {
955     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
956     if (!motd) {
957       COMMAND_REPLY_ERROR;
958       goto out;
959     }
960
961     i = 0;
962     cp = motd;
963     while(cp[i] != 0) {
964       if (cp[i++] == '\n') {
965         memset(line, 0, sizeof(line));
966         strncat(line, cp, i - 1);
967         cp += i;
968         
969         if (i == 2)
970           line[0] = ' ';
971         
972         cmd->client->ops->say(cmd->client, conn, "%s", line);
973         
974         if (!strlen(cp))
975           break;
976         i = 0;
977       }
978     }
979   }
980
981   /* Notify application */
982   COMMAND_REPLY((ARGS, motd));
983
984   /* Execute any pending command callbacks */
985   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
986
987  out:
988   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
989   silc_client_command_reply_free(cmd);
990 }
991
992 SILC_CLIENT_CMD_REPLY_FUNC(umode)
993 {
994 }
995
996 /* Received reply for CMODE command. */
997
998 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
999 {
1000   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1001   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1002   SilcCommandStatus status;
1003   unsigned char *tmp;
1004
1005   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1006   if (status != SILC_STATUS_OK) {
1007     cmd->client->ops->say(cmd->client, conn,
1008              "%s", silc_client_command_status_message(status));
1009     COMMAND_REPLY_ERROR;
1010     goto out;
1011   }
1012
1013   /* Get channel mode */
1014   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1015   if (!tmp) {
1016     COMMAND_REPLY_ERROR;
1017     goto out;
1018   }
1019
1020   /* Notify application */
1021   COMMAND_REPLY((ARGS, tmp));
1022
1023   /* Execute any pending command callbacks */
1024   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1025
1026  out:
1027   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1028   silc_client_command_reply_free(cmd);
1029 }
1030
1031 /* Received reply for CUMODE command */
1032
1033 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1034 {
1035   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1036   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1037   SilcCommandStatus status;
1038   SilcIDCacheEntry id_cache = NULL;
1039   SilcClientID *client_id;
1040   unsigned char *tmp, *id;
1041   unsigned int len;
1042   
1043   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1044   if (status != SILC_STATUS_OK) {
1045     cmd->client->ops->say(cmd->client, conn,
1046              "%s", silc_client_command_status_message(status));
1047     COMMAND_REPLY_ERROR;
1048     goto out;
1049   }
1050   
1051   /* Get channel mode */
1052   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1053   if (!tmp) {
1054     COMMAND_REPLY_ERROR;
1055     goto out;
1056   }
1057
1058   /* Get Client ID */
1059   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1060   if (!id) {
1061     COMMAND_REPLY_ERROR;
1062     goto out;
1063   }
1064   client_id = silc_id_payload_parse_id(id, len);
1065   if (!client_id) {
1066     COMMAND_REPLY_ERROR;
1067     goto out;
1068   }
1069   
1070   /* Get client entry */
1071   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1072                                    SILC_ID_CLIENT, &id_cache)) {
1073     COMMAND_REPLY_ERROR;
1074     goto out;
1075   }
1076
1077   /* Notify application */
1078   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1079   silc_free(client_id);
1080   
1081   /* Execute any pending command callbacks */
1082   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1083
1084  out:
1085   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1086   silc_client_command_reply_free(cmd);
1087 }
1088
1089 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1090 {
1091   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1092   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1093   SilcCommandStatus status;
1094   unsigned char *tmp;
1095
1096   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1097   SILC_GET16_MSB(status, tmp);
1098   if (status != SILC_STATUS_OK) {
1099     cmd->client->ops->say(cmd->client, conn,
1100              "%s", silc_client_command_status_message(status));
1101     COMMAND_REPLY_ERROR;
1102     goto out;
1103   }
1104
1105   /* Notify application */
1106   COMMAND_REPLY((ARGS));
1107
1108   /* Execute any pending command callbacks */
1109   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1110
1111  out:
1112   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1113   silc_client_command_reply_free(cmd);
1114 }
1115
1116 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1117 {
1118 }
1119
1120 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1121 {
1122 }
1123
1124 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1125 {
1126   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1127   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1128   SilcCommandStatus status;
1129   unsigned char *tmp;
1130
1131   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1132   SILC_GET16_MSB(status, tmp);
1133   if (status != SILC_STATUS_OK) {
1134     cmd->client->ops->say(cmd->client, conn,
1135              "%s", silc_client_command_status_message(status));
1136     COMMAND_REPLY_ERROR;
1137     goto out;
1138   }
1139
1140   /* Notify application */
1141   COMMAND_REPLY((ARGS));
1142
1143   /* Execute any pending command callbacks */
1144   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1145
1146  out:
1147   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1148   silc_client_command_reply_free(cmd);
1149 }
1150
1151 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1152 {
1153   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1154   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1155   SilcCommandStatus status;
1156   unsigned char *tmp;
1157
1158   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1159   SILC_GET16_MSB(status, tmp);
1160   if (status != SILC_STATUS_OK) {
1161     cmd->client->ops->say(cmd->client, conn,
1162              "%s", silc_client_command_status_message(status));
1163     COMMAND_REPLY_ERROR;
1164     goto out;
1165   }
1166
1167   /* Notify application */
1168   COMMAND_REPLY((ARGS));
1169
1170   /* Execute any pending command callbacks */
1171   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1172
1173  out:
1174   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1175   silc_client_command_reply_free(cmd);
1176 }
1177  
1178 SILC_CLIENT_CMD_REPLY_FUNC(close)
1179 {
1180   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1181   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1182   SilcCommandStatus status;
1183   unsigned char *tmp;
1184
1185   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1186   SILC_GET16_MSB(status, tmp);
1187   if (status != SILC_STATUS_OK) {
1188     cmd->client->ops->say(cmd->client, conn,
1189              "%s", silc_client_command_status_message(status));
1190     COMMAND_REPLY_ERROR;
1191     goto out;
1192   }
1193
1194   /* Notify application */
1195   COMMAND_REPLY((ARGS));
1196
1197   /* Execute any pending command callbacks */
1198   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1199
1200  out:
1201   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1202   silc_client_command_reply_free(cmd);
1203 }
1204  
1205 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1206 {
1207   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1208   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1209   SilcCommandStatus status;
1210   unsigned char *tmp;
1211
1212   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1213   SILC_GET16_MSB(status, tmp);
1214   if (status != SILC_STATUS_OK) {
1215     cmd->client->ops->say(cmd->client, conn,
1216              "%s", silc_client_command_status_message(status));
1217     COMMAND_REPLY_ERROR;
1218     goto out;
1219   }
1220
1221   /* Notify application */
1222   COMMAND_REPLY((ARGS));
1223
1224   /* Execute any pending command callbacks */
1225   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1226
1227  out:
1228   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1229   silc_client_command_reply_free(cmd);
1230 }
1231  
1232 /* Reply to LEAVE command. */
1233
1234 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1235 {
1236   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1237   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1238   SilcCommandStatus status;
1239   unsigned char *tmp;
1240
1241   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1242   SILC_GET16_MSB(status, tmp);
1243   if (status != SILC_STATUS_OK) {
1244     cmd->client->ops->say(cmd->client, conn,
1245              "%s", silc_client_command_status_message(status));
1246     COMMAND_REPLY_ERROR;
1247     goto out;
1248   }
1249
1250   /* Notify application */
1251   COMMAND_REPLY((ARGS));
1252
1253   /* Execute any pending command callbacks */
1254   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1255
1256  out:
1257   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1258   silc_client_command_reply_free(cmd);
1259 }
1260
1261 /* Reply to USERS command. Received list of client ID's and theirs modes
1262    on the channel we requested. */
1263
1264 SILC_CLIENT_CMD_REPLY_FUNC(users)
1265 {
1266   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1267   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1268   SilcCommandStatus status;
1269   SilcIDCacheEntry id_cache = NULL;
1270   SilcChannelEntry channel;
1271   SilcChannelUser chu;
1272   SilcChannelID *channel_id = NULL;
1273   SilcBuffer client_id_list;
1274   SilcBuffer client_mode_list;
1275   unsigned char *tmp;
1276   unsigned int tmp_len, list_count;
1277   int i;
1278   unsigned char **res_argv = NULL;
1279   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1280
1281   SILC_LOG_DEBUG(("Start"));
1282
1283   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1284   SILC_GET16_MSB(status, tmp);
1285   if (status != SILC_STATUS_OK) {
1286     cmd->client->ops->say(cmd->client, conn,
1287              "%s", silc_client_command_status_message(status));
1288     COMMAND_REPLY_ERROR;
1289     goto out;
1290   }
1291
1292   /* Get channel ID */
1293   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1294   if (!tmp)
1295     goto out;
1296   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1297   if (!channel_id)
1298     goto out;
1299
1300   /* Get the list count */
1301   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1302   if (!tmp)
1303     goto out;
1304   SILC_GET32_MSB(list_count, tmp);
1305
1306   /* Get Client ID list */
1307   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1308   if (!tmp)
1309     goto out;
1310
1311   client_id_list = silc_buffer_alloc(tmp_len);
1312   silc_buffer_pull_tail(client_id_list, tmp_len);
1313   silc_buffer_put(client_id_list, tmp, tmp_len);
1314
1315   /* Get client mode list */
1316   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1317   if (!tmp)
1318     goto out;
1319
1320   client_mode_list = silc_buffer_alloc(tmp_len);
1321   silc_buffer_pull_tail(client_mode_list, tmp_len);
1322   silc_buffer_put(client_mode_list, tmp, tmp_len);
1323
1324   /* Get channel entry */
1325   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1326                                    SILC_ID_CHANNEL, &id_cache)) {
1327     COMMAND_REPLY_ERROR;
1328     goto out;
1329   }
1330   channel = (SilcChannelEntry)id_cache->context;
1331
1332   /* Remove old client list from channel. */
1333   silc_list_start(channel->clients);
1334   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1335     silc_list_del(channel->clients, chu);
1336     silc_free(chu);
1337   }
1338
1339   /* Cache the received Client ID's and modes. This cache expires
1340      whenever server sends notify message to channel. It means two things;
1341      some user has joined or leaved the channel. XXX! */
1342   for (i = 0; i < list_count; i++) {
1343     unsigned short idp_len;
1344     unsigned int mode;
1345     SilcClientID *client_id;
1346     SilcClientEntry client;
1347
1348     /* Client ID */
1349     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1350     idp_len += 4;
1351     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1352     if (!client_id)
1353       continue;
1354
1355     /* Mode */
1356     SILC_GET32_MSB(mode, client_mode_list->data);
1357
1358     /* Check if we have this client cached already. */
1359     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1360                                      SILC_ID_CLIENT, &id_cache)) {
1361       /* No we don't have it, query it from the server. Assemble argument
1362          table that will be sent fr the IDENTIFY command later. */
1363       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1364                               (res_argc + 1));
1365       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1366                                    (res_argc + 1));
1367       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1368                                     (res_argc + 1));
1369       res_argv[res_argc] = client_id_list->data;
1370       res_argv_lens[res_argc] = idp_len;
1371       res_argv_types[res_argc] = res_argc + 3;
1372       res_argc++;
1373     } else {
1374       /* Found the client, join it to the channel */
1375       client = (SilcClientEntry)id_cache->context;
1376       chu = silc_calloc(1, sizeof(*chu));
1377       chu->client = client;
1378       chu->mode = mode;
1379       silc_list_add(channel->clients, chu);
1380
1381       silc_free(client_id);
1382       id_cache = NULL;
1383     }
1384
1385     silc_buffer_pull(client_id_list, idp_len);
1386     silc_buffer_pull(client_mode_list, 4);
1387   }
1388
1389   /* Query the client information from server if the list included clients
1390      that we don't know about. */
1391   if (res_argc) {
1392     SilcBuffer res_cmd;
1393
1394     /* Send the IDENTIFY command to server */
1395     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1396                                           res_argc, res_argv, res_argv_lens,
1397                                           res_argv_types, ++conn->cmd_ident);
1398     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1399                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1400                             TRUE);
1401
1402     /* Register pending command callback. After we've received the IDENTIFY
1403        command reply we will reprocess this command reply by re-calling this
1404        USERS command reply callback. */
1405     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1406                                 NULL, silc_client_command_reply_users, cmd);
1407
1408     silc_buffer_free(res_cmd);
1409     if (channel_id)
1410       silc_free(channel_id);
1411
1412     silc_free(res_argv);
1413     silc_free(res_argv_lens);
1414     silc_free(res_argv_types);
1415     return;
1416   }
1417
1418   /* Notify application */
1419   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1420
1421   /* Execute any pending command callbacks */
1422   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1423
1424   silc_buffer_free(client_id_list);
1425   silc_buffer_free(client_mode_list);
1426
1427  out:
1428   if (channel_id)
1429     silc_free(channel_id);
1430   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1431   silc_client_command_reply_free(cmd);
1432 }