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