Added support for multiple nickname on same channel.
[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 /* $Id$ */
25
26 #include "clientlibincludes.h"
27
28 /* Client command reply list. */
29 SilcClientCommandReply silc_command_reply_list[] =
30 {
31   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
32   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
33   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
34   SILC_CLIENT_CMD_REPLY(nick, NICK),
35   SILC_CLIENT_CMD_REPLY(list, LIST),
36   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
37   SILC_CLIENT_CMD_REPLY(invite, INVITE),
38   SILC_CLIENT_CMD_REPLY(quit, QUIT),
39   SILC_CLIENT_CMD_REPLY(kill, KILL),
40   SILC_CLIENT_CMD_REPLY(info, INFO),
41   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
42   SILC_CLIENT_CMD_REPLY(ping, PING),
43   SILC_CLIENT_CMD_REPLY(oper, OPER),
44   SILC_CLIENT_CMD_REPLY(join, JOIN),
45   SILC_CLIENT_CMD_REPLY(motd, MOTD),
46   SILC_CLIENT_CMD_REPLY(umode, UMODE),
47   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
48   SILC_CLIENT_CMD_REPLY(kick, KICK),
49   SILC_CLIENT_CMD_REPLY(restart, RESTART),
50   SILC_CLIENT_CMD_REPLY(close, CLOSE),
51   SILC_CLIENT_CMD_REPLY(die, DIE),
52   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
53   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
54   SILC_CLIENT_CMD_REPLY(names, NAMES),
55
56   { NULL, 0 },
57 };
58
59 /* Status message structure. Messages are defined below. */
60 typedef struct {
61   SilcCommandStatus status;
62   char *message;
63 } SilcCommandStatusMessage;
64
65 /* Status messages returned by the server */
66 #define STAT(x) SILC_STATUS_ERR_##x
67 const SilcCommandStatusMessage silc_command_status_messages[] = {
68
69   { STAT(NO_SUCH_NICK),      "No such nickname" },
70   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
71   { STAT(NO_SUCH_SERVER),    "No such server" },
72   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
73   { STAT(NO_RECIPIENT),      "No recipient given" },
74   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
75   { STAT(WILDCARDS),         "Unknown command" },
76   { STAT(NO_CLIENT_ID),      "No Client ID given" },
77   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
78   { STAT(NO_SERVER_ID),      "No Server ID given" },
79   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
80   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
81   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
82   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
83   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
84   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
85   { STAT(USER_ON_CHANNEL),   "User already on channel" },
86   { STAT(NOT_REGISTERED),    "You have not registered" },
87   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
88   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
89   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
90   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
91   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
92   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
93   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
94   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
95   { STAT(UNKNOWN_MODE),    "Unknown mode" },
96   { STAT(NOT_YOU),         "Cannot change mode for other users" },
97   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
98   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
99   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
100   { STAT(BAD_NICKNAME),    "Bad nickname" },
101   { STAT(BAD_CHANNEL),     "Bad channel name" },
102   { STAT(AUTH_FAILED),     "Authentication failed" },
103
104   { 0, NULL }
105 };
106
107 /* Command reply operation that is called at the end of all command replys. 
108    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
109 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
110 #define ARGS cmd->client, cmd->sock->user_data, \
111              cmd->payload, TRUE, status, silc_command_get(cmd->payload)
112
113 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
114 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
115   cmd->sock->user_data, cmd->payload, FALSE, status, \
116   silc_command_get(cmd->payload))
117
118 /* Process received command reply. */
119
120 void silc_client_command_reply_process(SilcClient client,
121                                        SilcSocketConnection sock,
122                                        SilcPacketContext *packet)
123 {
124   SilcBuffer buffer = packet->buffer;
125   SilcClientCommandReplyContext ctx;
126   SilcCommandPayload payload;
127
128   /* Get command reply payload from packet */
129   payload = silc_command_parse_payload(buffer);
130   if (!payload) {
131     /* Silently ignore bad reply packet */
132     SILC_LOG_DEBUG(("Bad command reply packet"));
133     return;
134   }
135   
136   /* Allocate command reply context. This must be free'd by the
137      command reply routine receiving it. */
138   ctx = silc_calloc(1, sizeof(*ctx));
139   ctx->client = client;
140   ctx->sock = sock;
141   ctx->payload = payload;
142   ctx->packet = packet;
143       
144   /* Check for pending commands and mark to be exeucted */
145   SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
146   
147   /* Execute command reply */
148   SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
149 }
150
151 /* Returns status message string */
152
153 static char *
154 silc_client_command_status_message(SilcCommandStatus status)
155 {
156   int i;
157
158   for (i = 0; silc_command_status_messages[i].message; i++) {
159     if (silc_command_status_messages[i].status == status)
160       break;
161   }
162
163   if (silc_command_status_messages[i].message == NULL)
164     return NULL;
165
166   return silc_command_status_messages[i].message;
167 }
168
169 /* Free command reply context and its internals. */
170
171 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
172 {
173   if (cmd) {
174     silc_command_free_payload(cmd->payload);
175     silc_free(cmd);
176   }
177 }
178
179 static void 
180 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
181                                       SilcCommandStatus status)
182 {
183   char buf[256];
184   int argc, len;
185   unsigned char *id_data;
186   char *nickname = NULL, *username = NULL;
187   char *realname = NULL;
188   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
189   
190   memset(buf, 0, sizeof(buf));
191   
192   argc = silc_command_get_arg_num(cmd->payload);
193   id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
194   
195   nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
196   if (nickname) {
197     strncat(buf, nickname, len);
198     strncat(buf, " is ", 4);
199   }
200   
201   username = silc_command_get_arg_type(cmd->payload, 4, &len);
202   if (username) {
203     strncat(buf, username, len);
204   }
205   
206   realname = silc_command_get_arg_type(cmd->payload, 5, &len);
207   if (realname) {
208     strncat(buf, " (", 2);
209     strncat(buf, realname, len);
210     strncat(buf, ")", 1);
211   }
212   
213   cmd->client->ops->say(cmd->client, conn, "%s", buf);
214   
215   /* Notify application */
216   COMMAND_REPLY((ARGS));
217 }
218
219 /* Received reply for WHOIS command. This maybe called several times
220    for one WHOIS command as server may reply with list of results. */
221 /* Sends to application: (no arguments) */
222
223 SILC_CLIENT_CMD_REPLY_FUNC(whois)
224 {
225   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
226   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
227   SilcCommandStatus status;
228   unsigned char *tmp;
229
230   SILC_LOG_DEBUG(("Start"));
231
232   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
233   SILC_GET16_MSB(status, tmp);
234   if (status != SILC_STATUS_OK && 
235       status != SILC_STATUS_LIST_START &&
236       status != SILC_STATUS_LIST_ITEM &&
237       status != SILC_STATUS_LIST_END) {
238     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
239       /* Take nickname which may be provided */
240       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
241       if (tmp)
242         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
243                  silc_client_command_status_message(status));
244       else
245         cmd->client->ops->say(cmd->client, conn, "%s",
246                  silc_client_command_status_message(status));
247       COMMAND_REPLY_ERROR;
248       goto out;
249     } else {
250       cmd->client->ops->say(cmd->client, conn,
251                "%s", silc_client_command_status_message(status));
252       COMMAND_REPLY_ERROR;
253       goto out;
254     }
255   }
256
257   /* Display one whois reply */
258   if (status == SILC_STATUS_OK) {
259     silc_client_command_reply_whois_print(cmd, status);
260   }
261
262   /* XXX list should not be displayed untill all items has been received. */
263   if (status == SILC_STATUS_LIST_START) {
264     silc_client_command_reply_whois_print(cmd, status);
265   }
266
267   if (status == SILC_STATUS_LIST_ITEM) {
268     silc_client_command_reply_whois_print(cmd, status);
269   }
270
271   if (status == SILC_STATUS_LIST_END) {
272     silc_client_command_reply_whois_print(cmd, status);
273   }
274
275   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
276
277  out:
278   silc_client_command_reply_free(cmd);
279 }
280
281 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
282 {
283 }
284
285 /* Received reply for IDENTIFY command. This maybe called several times
286    for one IDENTIFY command as server may reply with list of results. 
287    This is totally silent and does not print anything on screen. */
288
289 SILC_CLIENT_CMD_REPLY_FUNC(identify)
290 {
291   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
292   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
293   SilcClientEntry client_entry;
294   SilcCommandStatus status;
295   unsigned char *tmp;
296
297   SILC_LOG_DEBUG(("Start"));
298
299   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
300   SILC_GET16_MSB(status, tmp);
301   if (status != SILC_STATUS_OK) {
302     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
303       /* Take nickname which may be provided */
304       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
305       if (tmp)
306         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
307                  silc_client_command_status_message(status));
308       else
309         cmd->client->ops->say(cmd->client, conn, "%s",
310                  silc_client_command_status_message(status));
311       COMMAND_REPLY_ERROR;
312       goto out;
313     } else {
314       cmd->client->ops->say(cmd->client, conn,
315                "%s", silc_client_command_status_message(status));
316       COMMAND_REPLY_ERROR;
317       goto out;
318     }
319   }
320
321   /* Display one whois reply */
322   if (status == SILC_STATUS_OK) {
323     unsigned char *id_data;
324     char *nickname;
325
326     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
327     nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
328
329     /* Allocate client entry */
330     client_entry = silc_calloc(1, sizeof(*client_entry));
331     client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
332     client_entry->nickname = strdup(nickname);
333
334     /* Save received Client ID to ID cache */
335     silc_idcache_add(conn->client_cache, client_entry->nickname,
336                      SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
337   }
338
339   if (status == SILC_STATUS_LIST_START) {
340
341   }
342
343   if (status == SILC_STATUS_LIST_END) {
344
345   }
346
347   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
348
349  out:
350   silc_client_command_reply_free(cmd);
351 }
352
353 /* Received reply for command NICK. If everything went without errors
354    we just received our new Client ID. */
355 /* Sends to application: char * (nickname). */
356
357 SILC_CLIENT_CMD_REPLY_FUNC(nick)
358 {
359   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
360   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
361   SilcCommandStatus status;
362   unsigned char *tmp, *id_string;
363   int argc;
364
365   SILC_LOG_DEBUG(("Start"));
366
367   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
368   SILC_GET16_MSB(status, tmp);
369   if (status != SILC_STATUS_OK) {
370     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
371              silc_client_command_status_message(status));
372     COMMAND_REPLY_ERROR;
373     goto out;
374   }
375
376   argc = silc_command_get_arg_num(cmd->payload);
377   if (argc < 2 || argc > 2) {
378     cmd->client->ops->say(cmd->client, conn, 
379                           "Cannot set nickname: bad reply to command");
380     COMMAND_REPLY_ERROR;
381     goto out;
382   }
383
384   /* Take received Client ID */
385   id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
386   silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
387
388   /* Notify application */
389   COMMAND_REPLY((ARGS, conn->nickname));
390
391  out:
392   silc_client_command_reply_free(cmd);
393 }
394
395 SILC_CLIENT_CMD_REPLY_FUNC(list)
396 {
397 }
398
399 /* Received reply to topic command. */
400 /* Sends to application: SilcChannelID * (channel_id) and char * (topic) */
401
402 SILC_CLIENT_CMD_REPLY_FUNC(topic)
403 {
404   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
405   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
406   SilcCommandStatus status;
407   SilcChannelEntry channel;
408   SilcChannelID *channel_id = NULL;
409   SilcIDCacheEntry id_cache = NULL;
410   unsigned char *tmp, *id_string;
411   char *topic;
412   int argc;
413
414   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
415   SILC_GET16_MSB(status, tmp);
416   if (status != SILC_STATUS_OK) {
417     cmd->client->ops->say(cmd->client, conn,
418              "%s", silc_client_command_status_message(status));
419     silc_client_command_reply_free(cmd);
420     COMMAND_REPLY_ERROR;
421     return;
422   }
423
424   argc = silc_command_get_arg_num(cmd->payload);
425   if (argc < 1 || argc > 3) {
426     COMMAND_REPLY_ERROR;
427     goto out;
428   }
429
430   /* Take Channel ID */
431   id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
432   if (!id_string)
433     goto out;
434
435   channel_id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
436
437   /* Take topic */
438   topic = silc_command_get_arg_type(cmd->payload, 3, NULL);
439   if (!topic)
440     goto out;
441
442   /* Get the channel name */
443   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
444                                    SILC_ID_CHANNEL, &id_cache)) {
445     COMMAND_REPLY_ERROR;
446     goto out;
447   }
448   
449   channel = (SilcChannelEntry)id_cache->context;
450
451   cmd->client->ops->say(cmd->client, conn, 
452                         "Topic on channel %s: %s", channel->channel_name,
453                         topic);
454
455   /* Notify application */
456   COMMAND_REPLY((ARGS, channel_id, topic));
457
458  out:
459   if (channel_id)
460     silc_free(channel_id);
461   silc_client_command_reply_free(cmd);
462 }
463
464 /* Received reply to invite command. */
465 /* Sends to application: (no arguments) */
466
467 SILC_CLIENT_CMD_REPLY_FUNC(invite)
468 {
469   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
470   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
471   SilcCommandStatus status;
472   unsigned char *tmp;
473
474   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
475   SILC_GET16_MSB(status, tmp);
476   if (status != SILC_STATUS_OK) {
477     cmd->client->ops->say(cmd->client, conn,
478              "%s", silc_client_command_status_message(status));
479     silc_client_command_reply_free(cmd);
480     COMMAND_REPLY_ERROR;
481     return;
482   }
483
484   /* Notify application */
485   COMMAND_REPLY((ARGS));
486
487   silc_client_command_reply_free(cmd);
488 }
489  
490 SILC_CLIENT_CMD_REPLY_FUNC(quit)
491 {
492 }
493
494 SILC_CLIENT_CMD_REPLY_FUNC(kill)
495 {
496 }
497
498 /* Received reply to INFO command. We receive the server ID and some
499    information about the server user requested. */
500 /* Sends to application: char * (server information) */
501
502 SILC_CLIENT_CMD_REPLY_FUNC(info)
503 {
504   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
505   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
506   SilcClient client = cmd->client;
507   SilcCommandStatus status;
508   unsigned char *tmp;
509
510   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
511   SILC_GET16_MSB(status, tmp);
512   if (status != SILC_STATUS_OK) {
513     cmd->client->ops->say(cmd->client, conn,
514              "%s", silc_client_command_status_message(status));
515     silc_client_command_reply_free(cmd);
516     COMMAND_REPLY_ERROR;
517     return;
518   }
519
520   /* Get server ID */
521   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
522   if (!tmp)
523     goto out;
524
525   /* XXX save server id */
526
527   /* Get server info */
528   tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
529   if (!tmp)
530     goto out;
531
532   client->ops->say(cmd->client, conn, "Info: %s", tmp);
533
534   /* Notify application */
535   COMMAND_REPLY((ARGS, (char *)tmp));
536
537  out:
538   silc_client_command_reply_free(cmd);
539 }
540
541 SILC_CLIENT_CMD_REPLY_FUNC(connect)
542 {
543 }
544
545 /* Received reply to PING command. The reply time is shown to user. */
546 /* Sends to application: (no arguments) */
547
548 SILC_CLIENT_CMD_REPLY_FUNC(ping)
549 {
550   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
551   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
552   SilcCommandStatus status;
553   void *id;
554   char *tmp;
555   int i;
556   time_t diff, curtime;
557
558   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
559   SILC_GET16_MSB(status, tmp);
560   if (status != SILC_STATUS_OK) {
561     cmd->client->ops->say(cmd->client, conn,
562              "%s", silc_client_command_status_message(status));
563     COMMAND_REPLY_ERROR;
564     goto out;
565   }
566
567   curtime = time(NULL);
568   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
569
570   for (i = 0; i < conn->ping_count; i++) {
571     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
572       diff = curtime - conn->ping[i].start_time;
573       cmd->client->ops->say(cmd->client, conn, 
574                             "Ping reply from %s: %d second%s", 
575                             conn->ping[i].dest_name, diff, 
576                             diff == 1 ? "" : "s");
577
578       conn->ping[i].start_time = 0;
579       silc_free(conn->ping[i].dest_id);
580       conn->ping[i].dest_id = NULL;
581       silc_free(conn->ping[i].dest_name);
582       conn->ping[i].dest_name = NULL;
583
584       /* Notify application */
585       COMMAND_REPLY((ARGS));
586       goto out;
587     }
588   }
589
590  out:
591   silc_client_command_reply_free(cmd);
592 }
593
594 SILC_CLIENT_CMD_REPLY_FUNC(oper)
595 {
596 }
597
598 /* Received reply for JOIN command. */
599
600 SILC_CLIENT_CMD_REPLY_FUNC(join)
601 {
602   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
603   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
604   SilcClient client = cmd->client;
605   SilcCommandStatus status;
606   unsigned int argc, mode;
607   unsigned char *id_string;
608   char *topic, *tmp, *channel_name;
609
610   SILC_LOG_DEBUG(("Start"));
611
612   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
613   SILC_GET16_MSB(status, tmp);
614   if (status != SILC_STATUS_OK) {
615     cmd->client->ops->say(cmd->client, conn,
616              "%s", silc_client_command_status_message(status));
617     COMMAND_REPLY_ERROR;
618     goto out;
619   }
620
621   argc = silc_command_get_arg_num(cmd->payload);
622   if (argc < 3 || argc > 5) {
623     cmd->client->ops->say(cmd->client, conn,
624              "Cannot join channel: Bad reply packet");
625     COMMAND_REPLY_ERROR;
626     goto out;
627   }
628
629   /* Get channel name */
630   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
631   if (!tmp) {
632     cmd->client->ops->say(cmd->client, conn, 
633                           "Cannot join channel: Bad reply packet");
634     COMMAND_REPLY_ERROR;
635     goto out;
636   }
637   channel_name = strdup(tmp);
638
639   /* Get Channel ID */
640   id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
641   if (!id_string) {
642     cmd->client->ops->say(cmd->client, conn, 
643                           "Cannot join channel: Bad reply packet");
644     COMMAND_REPLY_ERROR;
645     goto out;
646   }
647
648   /* Get channel mode */
649   tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
650   if (tmp)
651     SILC_GET32_MSB(mode, tmp);
652   else
653     mode = 0;
654
655   /* Get topic */
656   topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
657
658   /* Save received Channel ID */
659   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
660                              mode, id_string);
661
662   if (topic)
663     client->ops->say(cmd->client, conn, 
664                      "Topic for %s: %s", channel_name, topic);
665
666   /* Notify application */
667   COMMAND_REPLY((ARGS, channel_name, topic));
668
669  out:
670   silc_client_command_reply_free(cmd);
671 }
672
673 /* Received reply for MOTD command */
674 /* Sends to application: char * (motd) */
675
676 SILC_CLIENT_CMD_REPLY_FUNC(motd)
677 {
678   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
679   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
680   SilcCommandStatus status;
681   unsigned int argc, i;
682   unsigned char *tmp;
683   char *motd = NULL, *cp, line[256];
684
685   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
686   SILC_GET16_MSB(status, tmp);
687   if (status != SILC_STATUS_OK) {
688     cmd->client->ops->say(cmd->client, conn,
689              "%s", silc_client_command_status_message(status));
690     COMMAND_REPLY_ERROR;
691     return;
692   }
693
694   argc = silc_command_get_arg_num(cmd->payload);
695   if (argc > 2) {
696     COMMAND_REPLY_ERROR;
697     goto out;
698   }
699
700   if (argc == 2) {
701     motd = silc_command_get_arg_type(cmd->payload, 2, NULL);
702     if (!motd) {
703       COMMAND_REPLY_ERROR;
704       goto out;
705     }
706
707     i = 0;
708     cp = motd;
709     while(cp[i] != 0) {
710       if (cp[i++] == '\n') {
711         memset(line, 0, sizeof(line));
712         strncat(line, cp, i - 1);
713         cp += i;
714         
715         if (i == 2)
716           line[0] = ' ';
717         
718         cmd->client->ops->say(cmd->client, conn, "%s", line);
719         
720         if (!strlen(cp))
721           break;
722         i = 0;
723       }
724     }
725   }
726
727   /* Notify application */
728   COMMAND_REPLY((ARGS, motd));
729
730  out:
731   silc_client_command_reply_free(cmd);
732 }
733
734 SILC_CLIENT_CMD_REPLY_FUNC(umode)
735 {
736 }
737
738 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
739 {
740 }
741
742 SILC_CLIENT_CMD_REPLY_FUNC(kick)
743 {
744 }
745
746 SILC_CLIENT_CMD_REPLY_FUNC(restart)
747 {
748 }
749  
750 SILC_CLIENT_CMD_REPLY_FUNC(close)
751 {
752 }
753  
754 SILC_CLIENT_CMD_REPLY_FUNC(die)
755 {
756 }
757  
758 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
759 {
760 }
761
762 /* Reply to LEAVE command. */
763 /* Sends to application: (no arguments) */
764
765 SILC_CLIENT_CMD_REPLY_FUNC(leave)
766 {
767   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
768   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
769   SilcCommandStatus status;
770   unsigned char *tmp;
771
772   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
773   SILC_GET16_MSB(status, tmp);
774   if (status != SILC_STATUS_OK) {
775     cmd->client->ops->say(cmd->client, conn,
776              "%s", silc_client_command_status_message(status));
777     COMMAND_REPLY_ERROR;
778     return;
779   }
780
781   /* Notify application */
782   COMMAND_REPLY((ARGS));
783
784   silc_client_command_reply_free(cmd);
785 }
786
787 /* Reply to NAMES command. Received list of client names on the channel 
788    we requested. */
789
790 SILC_CLIENT_CMD_REPLY_FUNC(names)
791 {
792   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
793   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
794   SilcCommandStatus status;
795   SilcIDCacheEntry id_cache = NULL;
796   SilcChannelEntry channel;
797   SilcChannelID *channel_id = NULL;
798   SilcBuffer client_id_list;
799   unsigned char *tmp;
800   char *name_list, *cp;
801   int i, k, len1, len2, list_count = 0;
802
803   SILC_LOG_DEBUG(("Start"));
804
805   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
806   SILC_GET16_MSB(status, tmp);
807   if (status != SILC_STATUS_OK) {
808     cmd->client->ops->say(cmd->client, conn,
809              "%s", silc_client_command_status_message(status));
810     COMMAND_REPLY_ERROR;
811     goto out;
812   }
813
814   /* Get channel ID */
815   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
816   if (!tmp) {
817     cmd->client->ops->say(cmd->client, conn, 
818                           "Cannot get user list: Bad reply packet");
819     COMMAND_REPLY_ERROR;
820     goto out;
821   }
822   channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
823
824   /* Get the name list of the channel */
825   name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
826   if (!name_list) {
827     cmd->client->ops->say(cmd->client, conn, 
828                           "Cannot get user list: Bad reply packet");
829     COMMAND_REPLY_ERROR;
830     goto out;
831   }
832
833   /* Get Client ID list */
834   tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
835   if (!tmp) {
836     cmd->client->ops->say(cmd->client, conn, 
837                           "Cannot get user list: Bad reply packet");
838     COMMAND_REPLY_ERROR;
839     goto out;
840   }
841
842   client_id_list = silc_buffer_alloc(len2);
843   silc_buffer_pull_tail(client_id_list, len2);
844   silc_buffer_put(client_id_list, tmp, len2);
845
846   /* Get the channel name */
847   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
848                                    SILC_ID_CHANNEL, &id_cache)) {
849     COMMAND_REPLY_ERROR;
850     goto out;
851   }
852   
853   channel = (SilcChannelEntry)id_cache->context;
854
855   /* If there is pending command we know that user has called this command
856      and we will handle the name list differently. */
857   if (cmd->callback) {
858     /* We will resolve all the necessary information about the people
859        on the channel. Only after that will we display the user list. */
860     for (i = 0; i < len1; i++) {
861       /* XXX */
862
863     }
864     silc_client_command_pending_del(SILC_COMMAND_NAMES);
865   } else {
866     /* there is no pending callback it means that this command reply
867        has been received without calling the command, ie. server has sent
868        the reply without getting the command from us first. This happens
869        with SILC servers that sends NAMES reply after joining to a channel. */
870
871     /* Remove commas from list */
872     for (i = 0; i < len1; i++)
873       if (name_list[i] == ',') {
874         name_list[i] = ' ';
875         list_count++;
876       }
877     list_count++;
878   }
879
880   if (channel->clients) {
881     silc_free(channel->clients);
882     channel->clients = NULL;
883     channel->clients_count = 0;
884   }
885
886   /* Cache the received name list and client ID's. This cache expires
887      whenever server sends notify message to channel. It means two things;
888      some user has joined or leaved the channel. */
889   cp = name_list;
890   for (i = 0; i < list_count; i++) {
891     int nick_len = strcspn(name_list, " ");
892     char *nickname = silc_calloc(nick_len, sizeof(*nickname));
893     SilcClientID *client_id;
894     SilcClientEntry client;
895
896     memcpy(nickname, name_list, nick_len);
897     client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
898     silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
899
900     client = silc_calloc(1, sizeof(*client));
901     client->id = client_id;
902     client->nickname = nickname;
903
904     /* Add client to cache */
905     silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
906                      client_id, (void *)client, TRUE);
907     name_list = name_list + nick_len + 1;
908
909     /* Add client to channel */
910     channel->clients = silc_realloc(channel->clients, 
911                                     sizeof(*channel->clients) * 
912                                     (channel->clients_count + 1));
913     channel->clients[channel->clients_count] = client;
914     channel->clients_count++;
915   }
916
917   name_list = cp;
918   for (i = 0; i < list_count; i++) {
919     int c;
920     int nick_len = strcspn(name_list, " ");
921     char *nickname = silc_calloc(nick_len, sizeof(*nickname));
922     memcpy(nickname, name_list, nick_len);
923
924     for (c = 0, k = 0; k < channel->clients_count; k++) {
925       if (channel->clients[k] && 
926           !strncmp(channel->clients[k]->nickname, 
927                    nickname, strlen(channel->clients[k]->nickname))) {
928         char t[8];
929         
930         if (!c) {
931           c++;
932           continue;
933         }
934         
935         memset(t, 0, sizeof(t));
936         channel->clients[k]->nickname = 
937           silc_calloc(strlen(nickname) + 8, 
938                       sizeof(*channel->clients[k]->nickname));
939         strncat(channel->clients[k]->nickname, nickname, strlen(nickname));
940         snprintf(t, sizeof(t), "[%d]", c++);
941         strncat(channel->clients[k]->nickname, t, strlen(t));
942       }
943     }
944
945     silc_free(nickname);
946   }
947
948   name_list = NULL;
949   len1 = 0;
950   for (k = 0; k < channel->clients_count; k++) {
951     char *n = channel->clients[k]->nickname;
952     len2 = strlen(n);
953     len1 += len2;
954     name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 1));
955     memcpy(name_list + (len1 - len2), n, len2);
956     name_list[len1] = 0;
957     
958     if (k == channel->clients_count - 1)
959       break;
960     memcpy(name_list + len1, " ", 1);
961     len1++;
962   }
963
964   cmd->client->ops->say(cmd->client, conn,
965                         "Users on %s: %s", channel->channel_name, name_list);
966
967   silc_free(name_list);
968   silc_buffer_free(client_id_list);
969
970  out:
971   if (channel_id)
972     silc_free(channel_id);
973   silc_client_command_reply_free(cmd);
974 }