implemented KICK command
[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(quit, QUIT),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(restart, RESTART),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(die, DIE),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66
67   { NULL, 0 },
68 };
69
70 /* Status message structure. Messages are defined below. */
71 typedef struct {
72   SilcCommandStatus status;
73   char *message;
74 } SilcCommandStatusMessage;
75
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
79
80   { STAT(NO_SUCH_NICK),      "No such nickname" },
81   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
82   { STAT(NO_SUCH_SERVER),    "No such server" },
83   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
84   { STAT(NO_RECIPIENT),      "No recipient given" },
85   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
86   { STAT(WILDCARDS),         "Unknown command" },
87   { STAT(NO_CLIENT_ID),      "No Client ID given" },
88   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
89   { STAT(NO_SERVER_ID),      "No Server ID given" },
90   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
91   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
92   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
95   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
96   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
98   { STAT(NOT_REGISTERED),    "You have not registered" },
99   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
101   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
102   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
104   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
105   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
106   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107   { STAT(UNKNOWN_MODE),    "Unknown mode" },
108   { STAT(NOT_YOU),         "Cannot change mode for other users" },
109   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
111   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
112   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
113   { STAT(BAD_NICKNAME),    "Bad nickname" },
114   { STAT(BAD_CHANNEL),     "Bad channel name" },
115   { STAT(AUTH_FAILED),     "Authentication failed" },
116
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(quit)
630 {
631 }
632
633 SILC_CLIENT_CMD_REPLY_FUNC(kill)
634 {
635 }
636
637 /* Received reply to INFO command. We receive the server ID and some
638    information about the server user requested. */
639
640 SILC_CLIENT_CMD_REPLY_FUNC(info)
641 {
642   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
643   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
644   SilcClient client = cmd->client;
645   SilcCommandStatus status;
646   unsigned char *tmp;
647
648   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
649   SILC_GET16_MSB(status, tmp);
650   if (status != SILC_STATUS_OK) {
651     cmd->client->ops->say(cmd->client, conn,
652              "%s", silc_client_command_status_message(status));
653     COMMAND_REPLY_ERROR;
654     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
655     silc_client_command_reply_free(cmd);
656     return;
657   }
658
659   /* Get server ID */
660   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
661   if (!tmp)
662     goto out;
663
664   /* XXX save server id */
665
666   /* Get server info */
667   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
668   if (!tmp)
669     goto out;
670
671   client->ops->say(cmd->client, conn, "Info: %s", tmp);
672
673   /* Notify application */
674   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
675
676   /* Execute any pending command callbacks */
677   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
678
679  out:
680   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
681   silc_client_command_reply_free(cmd);
682 }
683
684 SILC_CLIENT_CMD_REPLY_FUNC(connect)
685 {
686 }
687
688 /* Received reply to PING command. The reply time is shown to user. */
689
690 SILC_CLIENT_CMD_REPLY_FUNC(ping)
691 {
692   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
693   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
694   SilcCommandStatus status;
695   void *id;
696   int i;
697   time_t diff, curtime;
698
699   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
700   if (status != SILC_STATUS_OK) {
701     cmd->client->ops->say(cmd->client, conn,
702              "%s", silc_client_command_status_message(status));
703     COMMAND_REPLY_ERROR;
704     goto out;
705   }
706
707   curtime = time(NULL);
708   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
709                       cmd->packet->src_id_type);
710   if (!id) {
711     COMMAND_REPLY_ERROR;
712     goto out;
713   }
714
715   for (i = 0; i < conn->ping_count; i++) {
716     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
717       diff = curtime - conn->ping[i].start_time;
718       cmd->client->ops->say(cmd->client, conn, 
719                             "Ping reply from %s: %d second%s", 
720                             conn->ping[i].dest_name, diff, 
721                             diff == 1 ? "" : "s");
722       
723       conn->ping[i].start_time = 0;
724       silc_free(conn->ping[i].dest_id);
725       conn->ping[i].dest_id = NULL;
726       silc_free(conn->ping[i].dest_name);
727       conn->ping[i].dest_name = NULL;
728       break;
729     }
730   }
731
732   silc_free(id);
733
734   /* Notify application */
735   COMMAND_REPLY((ARGS));
736
737   /* Execute any pending command callbacks */
738   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
739
740  out:
741   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
742   silc_client_command_reply_free(cmd);
743 }
744
745 SILC_CLIENT_CMD_REPLY_FUNC(oper)
746 {
747 }
748
749 /* Received reply for JOIN command. */
750
751 SILC_CLIENT_CMD_REPLY_FUNC(join)
752 {
753   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
754   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
755   SilcClient client = cmd->client;
756   SilcCommandStatus status;
757   SilcIDPayload idp = NULL;
758   unsigned int argc, mode, len;
759   char *topic, *tmp, *channel_name = NULL;
760   SilcBuffer keyp;
761
762   SILC_LOG_DEBUG(("Start"));
763
764   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
765   if (status != SILC_STATUS_OK) {
766     cmd->client->ops->say(cmd->client, conn,
767              "%s", silc_client_command_status_message(status));
768     COMMAND_REPLY_ERROR;
769     goto out;
770   }
771
772   argc = silc_argument_get_arg_num(cmd->args);
773   if (argc < 3 || argc > 9) {
774     cmd->client->ops->say(cmd->client, conn,
775              "Cannot join channel: Bad reply packet");
776     COMMAND_REPLY_ERROR;
777     goto out;
778   }
779
780   /* Get channel name */
781   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
782   if (!tmp) {
783     cmd->client->ops->say(cmd->client, conn, 
784                           "Cannot join channel: Bad reply packet");
785     COMMAND_REPLY_ERROR;
786     goto out;
787   }
788   channel_name = strdup(tmp);
789
790   /* Get Channel ID */
791   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
792   if (!tmp) {
793     cmd->client->ops->say(cmd->client, conn, 
794                           "Cannot join channel: Bad reply packet");
795     COMMAND_REPLY_ERROR;
796     silc_free(channel_name);
797     goto out;
798   }
799   idp = silc_id_payload_parse_data(tmp, len);
800   if (!idp) {
801     COMMAND_REPLY_ERROR;
802     silc_free(channel_name);
803     goto out;
804   }
805
806   /* Get channel mode */
807   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
808   if (tmp)
809     SILC_GET32_MSB(mode, tmp);
810   else
811     mode = 0;
812
813   /* Get channel key */
814   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
815   if (!tmp) {
816     silc_id_payload_free(idp);
817     silc_free(channel_name);
818     goto out;
819   }
820   keyp = silc_buffer_alloc(len);
821   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
822   silc_buffer_put(keyp, tmp, len);
823
824   /* Get topic */
825   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
826
827   /* Save received Channel ID */
828   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
829                              mode, idp);
830   silc_id_payload_free(idp);
831
832   /* Save channel key */
833   silc_client_save_channel_key(conn, keyp, conn->current_channel);
834   silc_buffer_free(keyp);
835
836   if (topic)
837     client->ops->say(cmd->client, conn, 
838                      "Topic for %s: %s", channel_name, topic);
839
840   /* Notify application */
841   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
842                  NULL, NULL, topic));
843
844   /* Execute any pending command callbacks */
845   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
846
847  out:
848   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
849   silc_client_command_reply_free(cmd);
850 }
851
852 /* Received reply for MOTD command */
853
854 SILC_CLIENT_CMD_REPLY_FUNC(motd)
855 {
856   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
857   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
858   SilcCommandStatus status;
859   unsigned int argc, i;
860   unsigned char *tmp;
861   char *motd = NULL, *cp, line[256];
862
863   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
864   SILC_GET16_MSB(status, tmp);
865   if (status != SILC_STATUS_OK) {
866     cmd->client->ops->say(cmd->client, conn,
867              "%s", silc_client_command_status_message(status));
868     COMMAND_REPLY_ERROR;
869     return;
870   }
871
872   argc = silc_argument_get_arg_num(cmd->args);
873   if (argc > 2) {
874     COMMAND_REPLY_ERROR;
875     goto out;
876   }
877
878   if (argc == 2) {
879     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
880     if (!motd) {
881       COMMAND_REPLY_ERROR;
882       goto out;
883     }
884
885     i = 0;
886     cp = motd;
887     while(cp[i] != 0) {
888       if (cp[i++] == '\n') {
889         memset(line, 0, sizeof(line));
890         strncat(line, cp, i - 1);
891         cp += i;
892         
893         if (i == 2)
894           line[0] = ' ';
895         
896         cmd->client->ops->say(cmd->client, conn, "%s", line);
897         
898         if (!strlen(cp))
899           break;
900         i = 0;
901       }
902     }
903   }
904
905   /* Notify application */
906   COMMAND_REPLY((ARGS, motd));
907
908   /* Execute any pending command callbacks */
909   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
910
911  out:
912   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
913   silc_client_command_reply_free(cmd);
914 }
915
916 SILC_CLIENT_CMD_REPLY_FUNC(umode)
917 {
918 }
919
920 /* Received reply for CMODE command. */
921
922 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
923 {
924   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
925   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
926   SilcCommandStatus status;
927   unsigned char *tmp;
928
929   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
930   if (status != SILC_STATUS_OK) {
931     cmd->client->ops->say(cmd->client, conn,
932              "%s", silc_client_command_status_message(status));
933     COMMAND_REPLY_ERROR;
934     goto out;
935   }
936
937   /* Get channel mode */
938   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
939   if (!tmp) {
940     COMMAND_REPLY_ERROR;
941     goto out;
942   }
943
944   /* Notify application */
945   COMMAND_REPLY((ARGS, tmp));
946
947   /* Execute any pending command callbacks */
948   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
949
950  out:
951   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
952   silc_client_command_reply_free(cmd);
953 }
954
955 /* Received reply for CUMODE command */
956
957 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
958 {
959   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
960   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
961   SilcCommandStatus status;
962   SilcIDCacheEntry id_cache = NULL;
963   SilcClientID *client_id;
964   unsigned char *tmp, *id;
965   unsigned int len;
966   
967   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
968   if (status != SILC_STATUS_OK) {
969     cmd->client->ops->say(cmd->client, conn,
970              "%s", silc_client_command_status_message(status));
971     COMMAND_REPLY_ERROR;
972     goto out;
973   }
974   
975   /* Get channel mode */
976   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
977   if (!tmp) {
978     COMMAND_REPLY_ERROR;
979     goto out;
980   }
981
982   /* Get Client ID */
983   id = silc_argument_get_arg_type(cmd->args, 3, &len);
984   if (!id) {
985     COMMAND_REPLY_ERROR;
986     goto out;
987   }
988   client_id = silc_id_payload_parse_id(id, len);
989   if (!client_id) {
990     COMMAND_REPLY_ERROR;
991     goto out;
992   }
993   
994   /* Get client entry */
995   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
996                                    SILC_ID_CLIENT, &id_cache)) {
997     COMMAND_REPLY_ERROR;
998     goto out;
999   }
1000
1001   /* Notify application */
1002   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1003   silc_free(client_id);
1004   
1005   /* Execute any pending command callbacks */
1006   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1007
1008  out:
1009   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1010   silc_client_command_reply_free(cmd);
1011 }
1012
1013 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1014 {
1015   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1016   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1017   SilcCommandStatus status;
1018   unsigned char *tmp;
1019
1020   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1021   SILC_GET16_MSB(status, tmp);
1022   if (status != SILC_STATUS_OK) {
1023     cmd->client->ops->say(cmd->client, conn,
1024              "%s", silc_client_command_status_message(status));
1025     COMMAND_REPLY_ERROR;
1026     goto out;
1027   }
1028
1029   /* Notify application */
1030   COMMAND_REPLY((ARGS));
1031
1032   /* Execute any pending command callbacks */
1033   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1034
1035  out:
1036   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1037   silc_client_command_reply_free(cmd);
1038 }
1039
1040 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1041 {
1042 }
1043  
1044 SILC_CLIENT_CMD_REPLY_FUNC(close)
1045 {
1046 }
1047  
1048 SILC_CLIENT_CMD_REPLY_FUNC(die)
1049 {
1050 }
1051  
1052 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1053 {
1054 }
1055
1056 /* Reply to LEAVE command. */
1057
1058 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1059 {
1060   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1061   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1062   SilcCommandStatus status;
1063   unsigned char *tmp;
1064
1065   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1066   SILC_GET16_MSB(status, tmp);
1067   if (status != SILC_STATUS_OK) {
1068     cmd->client->ops->say(cmd->client, conn,
1069              "%s", silc_client_command_status_message(status));
1070     COMMAND_REPLY_ERROR;
1071     goto out;
1072   }
1073
1074   /* Notify application */
1075   COMMAND_REPLY((ARGS));
1076
1077   /* Execute any pending command callbacks */
1078   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1079
1080  out:
1081   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1082   silc_client_command_reply_free(cmd);
1083 }
1084
1085 /* Reply to USERS command. Received list of client ID's and theirs modes
1086    on the channel we requested. */
1087
1088 SILC_CLIENT_CMD_REPLY_FUNC(users)
1089 {
1090   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1091   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1092   SilcCommandStatus status;
1093   SilcIDCacheEntry id_cache = NULL;
1094   SilcChannelEntry channel;
1095   SilcChannelUser chu;
1096   SilcChannelID *channel_id = NULL;
1097   SilcBuffer client_id_list;
1098   SilcBuffer client_mode_list;
1099   unsigned char *tmp;
1100   unsigned int tmp_len, list_count;
1101   int i;
1102   unsigned char **res_argv = NULL;
1103   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1104
1105   SILC_LOG_DEBUG(("Start"));
1106
1107   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1108   SILC_GET16_MSB(status, tmp);
1109   if (status != SILC_STATUS_OK) {
1110     cmd->client->ops->say(cmd->client, conn,
1111              "%s", silc_client_command_status_message(status));
1112     COMMAND_REPLY_ERROR;
1113     goto out;
1114   }
1115
1116   /* Get channel ID */
1117   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1118   if (!tmp)
1119     goto out;
1120   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1121   if (!channel_id)
1122     goto out;
1123
1124   /* Get the list count */
1125   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1126   if (!tmp)
1127     goto out;
1128   SILC_GET32_MSB(list_count, tmp);
1129
1130   /* Get Client ID list */
1131   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1132   if (!tmp)
1133     goto out;
1134
1135   client_id_list = silc_buffer_alloc(tmp_len);
1136   silc_buffer_pull_tail(client_id_list, tmp_len);
1137   silc_buffer_put(client_id_list, tmp, tmp_len);
1138
1139   /* Get client mode list */
1140   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1141   if (!tmp)
1142     goto out;
1143
1144   client_mode_list = silc_buffer_alloc(tmp_len);
1145   silc_buffer_pull_tail(client_mode_list, tmp_len);
1146   silc_buffer_put(client_mode_list, tmp, tmp_len);
1147
1148   /* Get channel entry */
1149   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1150                                    SILC_ID_CHANNEL, &id_cache)) {
1151     COMMAND_REPLY_ERROR;
1152     goto out;
1153   }
1154   channel = (SilcChannelEntry)id_cache->context;
1155
1156   /* Remove old client list from channel. */
1157   silc_list_start(channel->clients);
1158   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1159     silc_list_del(channel->clients, chu);
1160     silc_free(chu);
1161   }
1162
1163   /* Cache the received Client ID's and modes. This cache expires
1164      whenever server sends notify message to channel. It means two things;
1165      some user has joined or leaved the channel. XXX! */
1166   for (i = 0; i < list_count; i++) {
1167     unsigned short idp_len;
1168     unsigned int mode;
1169     SilcClientID *client_id;
1170     SilcClientEntry client;
1171
1172     /* Client ID */
1173     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1174     idp_len += 4;
1175     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1176     if (!client_id)
1177       continue;
1178
1179     /* Mode */
1180     SILC_GET32_MSB(mode, client_mode_list->data);
1181
1182     /* Check if we have this client cached already. */
1183     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1184                                      SILC_ID_CLIENT, &id_cache)) {
1185       /* No we don't have it, query it from the server. Assemble argument
1186          table that will be sent fr the IDENTIFY command later. */
1187       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1188                               (res_argc + 1));
1189       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1190                                    (res_argc + 1));
1191       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1192                                     (res_argc + 1));
1193       res_argv[res_argc] = client_id_list->data;
1194       res_argv_lens[res_argc] = idp_len;
1195       res_argv_types[res_argc] = res_argc + 3;
1196       res_argc++;
1197     } else {
1198       /* Found the client, join it to the channel */
1199       client = (SilcClientEntry)id_cache->context;
1200       chu = silc_calloc(1, sizeof(*chu));
1201       chu->client = client;
1202       chu->mode = mode;
1203       silc_list_add(channel->clients, chu);
1204
1205       silc_free(client_id);
1206       id_cache = NULL;
1207     }
1208
1209     silc_buffer_pull(client_id_list, idp_len);
1210     silc_buffer_pull(client_mode_list, 4);
1211   }
1212
1213   /* Query the client information from server if the list included clients
1214      that we don't know about. */
1215   if (res_argc) {
1216     SilcBuffer res_cmd;
1217
1218     /* Send the IDENTIFY command to server */
1219     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1220                                           res_argc, res_argv, res_argv_lens,
1221                                           res_argv_types, ++conn->cmd_ident);
1222     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1223                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1224                             TRUE);
1225
1226     /* Register pending command callback. After we've received the IDENTIFY
1227        command reply we will reprocess this command reply by re-calling this
1228        USERS command reply callback. */
1229     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1230                                 NULL, silc_client_command_reply_users, cmd);
1231
1232     silc_buffer_free(res_cmd);
1233     if (channel_id)
1234       silc_free(channel_id);
1235
1236     silc_free(res_argv);
1237     silc_free(res_argv_lens);
1238     silc_free(res_argv_types);
1239     return;
1240   }
1241
1242   /* We have all the clients on the channel cached now. Create a nice
1243      output for user interface and notify application. */
1244
1245   if (!cmd->callback) {
1246     /* Server has sent us USERS reply even when we haven't actually sent
1247        USERS command. This is normal behaviour when joining to a channel.
1248        Display some nice information on the user interface. */
1249     int k = 0, len1 = 0, len2 = 0;
1250     char *name_list = NULL;
1251
1252     silc_list_start(channel->clients);
1253     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1254       char *m, *n = chu->client->nickname;
1255       len2 = strlen(n);
1256       len1 += len2;
1257
1258       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1259
1260       m = silc_client_chumode_char(chu->mode);
1261       if (m) {
1262         memcpy(name_list + (len1 - len2), m, strlen(m));
1263         len1 += strlen(m);
1264         silc_free(m);
1265       }
1266
1267       memcpy(name_list + (len1 - len2), n, len2);
1268       name_list[len1] = 0;
1269       
1270       if (k == silc_list_count(channel->clients) - 1)
1271         break;
1272       memcpy(name_list + len1, " ", 1);
1273       len1++;
1274       k++;
1275     }
1276
1277     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1278                           channel->channel_name, name_list);
1279     silc_free(name_list);
1280   }
1281
1282   /* Notify application */
1283   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1284                  client_mode_list->head));
1285
1286   /* Execute any pending command callbacks */
1287   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1288
1289   silc_buffer_free(client_id_list);
1290   silc_buffer_free(client_mode_list);
1291
1292  out:
1293   if (channel_id)
1294     silc_free(channel_id);
1295   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1296   silc_client_command_reply_free(cmd);
1297 }