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