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