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