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