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