Created SILC Client Libary by moving stuff from silc/ directory.
[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, 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, silc_command_get(cmd->payload))
116
117 /* Process received command reply. */
118
119 void silc_client_command_reply_process(SilcClient client,
120                                        SilcSocketConnection sock,
121                                        SilcPacketContext *packet)
122 {
123   SilcBuffer buffer = packet->buffer;
124   SilcClientCommandReplyContext ctx;
125   SilcCommandPayload payload;
126
127   /* Get command reply payload from packet */
128   payload = silc_command_parse_payload(buffer);
129   if (!payload) {
130     /* Silently ignore bad reply packet */
131     SILC_LOG_DEBUG(("Bad command reply packet"));
132     return;
133   }
134   
135   /* Allocate command reply context. This must be free'd by the
136      command reply routine receiving it. */
137   ctx = silc_calloc(1, sizeof(*ctx));
138   ctx->client = client;
139   ctx->sock = sock;
140   ctx->payload = payload;
141   ctx->packet = packet;
142       
143   /* Check for pending commands and mark to be exeucted */
144   SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
145   
146   /* Execute command reply */
147   SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
148 }
149
150 /* Returns status message string */
151
152 static char *
153 silc_client_command_status_message(SilcCommandStatus status)
154 {
155   int i;
156
157   for (i = 0; silc_command_status_messages[i].message; i++) {
158     if (silc_command_status_messages[i].status == status)
159       break;
160   }
161
162   if (silc_command_status_messages[i].message == NULL)
163     return NULL;
164
165   return silc_command_status_messages[i].message;
166 }
167
168 /* Free command reply context and its internals. */
169
170 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
171 {
172   if (cmd) {
173     silc_command_free_payload(cmd->payload);
174     silc_free(cmd);
175   }
176 }
177
178 /* Received reply for WHOIS command. This maybe called several times
179    for one WHOIS command as server may reply with list of results. */
180 /* Sends to application: (no arguments) */
181
182 SILC_CLIENT_CMD_REPLY_FUNC(whois)
183 {
184   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
185   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
186   SilcCommandStatus status;
187   unsigned char *tmp;
188
189   SILC_LOG_DEBUG(("Start"));
190
191   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
192   SILC_GET16_MSB(status, tmp);
193   if (status != SILC_STATUS_OK) {
194     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
195       /* Take nickname which may be provided */
196       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
197       if (tmp)
198         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
199                  silc_client_command_status_message(status));
200       else
201         cmd->client->ops->say(cmd->client, conn, "%s",
202                  silc_client_command_status_message(status));
203       COMMAND_REPLY_ERROR;
204       goto out;
205     } else {
206       cmd->client->ops->say(cmd->client, conn,
207                "%s", silc_client_command_status_message(status));
208       COMMAND_REPLY_ERROR;
209       goto out;
210     }
211   }
212
213   /* Display one whois reply */
214   if (status == SILC_STATUS_OK) {
215     char buf[256];
216     int argc, len;
217     unsigned char *id_data;
218     char *nickname = NULL, *username = NULL;
219     char *realname = NULL;
220
221     memset(buf, 0, sizeof(buf));
222
223     argc = silc_command_get_arg_num(cmd->payload);
224     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
225
226     nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
227     if (nickname) {
228       strncat(buf, nickname, len);
229       strncat(buf, " is ", 4);
230     }
231
232     username = silc_command_get_arg_type(cmd->payload, 4, &len);
233     if (username) {
234       strncat(buf, username, len);
235     }
236
237     realname = silc_command_get_arg_type(cmd->payload, 5, &len);
238     if (realname) {
239       strncat(buf, " (", 2);
240       strncat(buf, realname, len);
241       strncat(buf, ")", 1);
242     }
243
244 #if 0
245     /* Save received Client ID to ID cache */
246     /* XXX Maybe should not be saved as /MSG will get confused */
247     id = silc_id_str2id(id_data, SILC_ID_CLIENT);
248     client->current_conn->client_id_cache_count[(int)nickname[0] - 32] =
249     silc_idcache_add(&client->current_conn->
250                      client_id_cache[(int)nickname[0] - 32],
251                      client->current_conn->
252                      client_id_cache_count[(int)nickname[0] - 32],
253                      strdup(nickname), SILC_ID_CLIENT, id, NULL);
254 #endif
255
256     cmd->client->ops->say(cmd->client, conn, "%s", buf);
257
258     /* Notify application */
259     COMMAND_REPLY((ARGS));
260   }
261
262   if (status == SILC_STATUS_LIST_START) {
263
264   }
265
266   if (status == SILC_STATUS_LIST_END) {
267
268   }
269
270   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
271
272  out:
273   silc_client_command_reply_free(cmd);
274 }
275
276 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
277 {
278 }
279
280 /* Received reply for IDENTIFY command. This maybe called several times
281    for one IDENTIFY command as server may reply with list of results. 
282    This is totally silent and does not print anything on screen. */
283
284 SILC_CLIENT_CMD_REPLY_FUNC(identify)
285 {
286   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
287   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
288   SilcClientEntry client_entry;
289   SilcCommandStatus status;
290   unsigned char *tmp;
291
292   SILC_LOG_DEBUG(("Start"));
293
294   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
295   SILC_GET16_MSB(status, tmp);
296   if (status != SILC_STATUS_OK) {
297     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
298       /* Take nickname which may be provided */
299       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
300       if (tmp)
301         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
302                  silc_client_command_status_message(status));
303       else
304         cmd->client->ops->say(cmd->client, conn, "%s",
305                  silc_client_command_status_message(status));
306       COMMAND_REPLY_ERROR;
307       goto out;
308     } else {
309       cmd->client->ops->say(cmd->client, conn,
310                "%s", silc_client_command_status_message(status));
311       COMMAND_REPLY_ERROR;
312       goto out;
313     }
314   }
315
316   /* Display one whois reply */
317   if (status == SILC_STATUS_OK) {
318     unsigned char *id_data;
319     char *nickname;
320
321     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
322     nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
323
324     /* Allocate client entry */
325     client_entry = silc_calloc(1, sizeof(*client_entry));
326     client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
327     client_entry->nickname = strdup(nickname);
328
329     /* Save received Client ID to ID cache */
330     silc_idcache_add(conn->client_cache, client_entry->nickname,
331                      SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
332   }
333
334   if (status == SILC_STATUS_LIST_START) {
335
336   }
337
338   if (status == SILC_STATUS_LIST_END) {
339
340   }
341
342   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
343
344  out:
345   silc_client_command_reply_free(cmd);
346 }
347
348 /* Received reply for command NICK. If everything went without errors
349    we just received our new Client ID. */
350 /* Sends to application: char * (nickname). */
351
352 SILC_CLIENT_CMD_REPLY_FUNC(nick)
353 {
354   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
355   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
356   SilcCommandStatus status;
357   unsigned char *tmp, *id_string;
358   int argc;
359
360   SILC_LOG_DEBUG(("Start"));
361
362   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
363   SILC_GET16_MSB(status, tmp);
364   if (status != SILC_STATUS_OK) {
365     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
366              silc_client_command_status_message(status));
367     COMMAND_REPLY_ERROR;
368     goto out;
369   }
370
371   argc = silc_command_get_arg_num(cmd->payload);
372   if (argc < 2 || argc > 2) {
373     cmd->client->ops->say(cmd->client, conn, 
374                           "Cannot set nickname: bad reply to command");
375     COMMAND_REPLY_ERROR;
376     goto out;
377   }
378
379   /* Take received Client ID */
380   id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
381   silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
382
383   /* Notify application */
384   COMMAND_REPLY((ARGS, conn->nickname));
385
386  out:
387   silc_client_command_reply_free(cmd);
388 }
389
390 SILC_CLIENT_CMD_REPLY_FUNC(list)
391 {
392 }
393
394 SILC_CLIENT_CMD_REPLY_FUNC(topic)
395 {
396 }
397
398 /* Received reply to invite command. */
399 /* Sends to application: (no arguments) */
400
401 SILC_CLIENT_CMD_REPLY_FUNC(invite)
402 {
403   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
404   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
405   SilcCommandStatus status;
406   unsigned char *tmp;
407
408   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
409   SILC_GET16_MSB(status, tmp);
410   if (status != SILC_STATUS_OK) {
411     cmd->client->ops->say(cmd->client, conn,
412              "%s", silc_client_command_status_message(status));
413     silc_client_command_reply_free(cmd);
414     COMMAND_REPLY_ERROR;
415     return;
416   }
417
418   /* Notify application */
419   COMMAND_REPLY((ARGS));
420
421   silc_client_command_reply_free(cmd);
422 }
423  
424 SILC_CLIENT_CMD_REPLY_FUNC(quit)
425 {
426 }
427
428 SILC_CLIENT_CMD_REPLY_FUNC(kill)
429 {
430 }
431
432 /* Received reply to INFO command. We receive the server ID and some
433    information about the server user requested. */
434 /* Sends to application: char * (server information) */
435
436 SILC_CLIENT_CMD_REPLY_FUNC(info)
437 {
438   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
439   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
440   SilcClient client = cmd->client;
441   SilcCommandStatus status;
442   unsigned char *tmp;
443
444   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
445   SILC_GET16_MSB(status, tmp);
446   if (status != SILC_STATUS_OK) {
447     cmd->client->ops->say(cmd->client, conn,
448              "%s", silc_client_command_status_message(status));
449     silc_client_command_reply_free(cmd);
450     COMMAND_REPLY_ERROR;
451     return;
452   }
453
454   /* Get server ID */
455   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
456   if (!tmp)
457     goto out;
458
459   /* XXX save server id */
460
461   /* Get server info */
462   tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
463   if (!tmp)
464     goto out;
465
466   client->ops->say(cmd->client, conn, "Info: %s", tmp);
467
468   /* Notify application */
469   COMMAND_REPLY((ARGS, (char *)tmp));
470
471  out:
472   silc_client_command_reply_free(cmd);
473 }
474
475 SILC_CLIENT_CMD_REPLY_FUNC(connect)
476 {
477 }
478
479 /* Received reply to PING command. The reply time is shown to user. */
480 /* Sends to application: (no arguments) */
481
482 SILC_CLIENT_CMD_REPLY_FUNC(ping)
483 {
484   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
485   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
486   SilcCommandStatus status;
487   void *id;
488   char *tmp;
489   int i;
490   time_t diff, curtime;
491
492   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
493   SILC_GET16_MSB(status, tmp);
494   if (status != SILC_STATUS_OK) {
495     cmd->client->ops->say(cmd->client, conn,
496              "%s", silc_client_command_status_message(status));
497     COMMAND_REPLY_ERROR;
498     goto out;
499   }
500
501   curtime = time(NULL);
502   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
503
504   for (i = 0; i < conn->ping_count; i++) {
505     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
506       diff = curtime - conn->ping[i].start_time;
507       cmd->client->ops->say(cmd->client, conn, 
508                             "Ping reply from %s: %d second%s", 
509                             conn->ping[i].dest_name, diff, 
510                             diff == 1 ? "" : "s");
511
512       conn->ping[i].start_time = 0;
513       silc_free(conn->ping[i].dest_id);
514       conn->ping[i].dest_id = NULL;
515       silc_free(conn->ping[i].dest_name);
516       conn->ping[i].dest_name = NULL;
517
518       /* Notify application */
519       COMMAND_REPLY((ARGS));
520       goto out;
521     }
522   }
523
524  out:
525   silc_client_command_reply_free(cmd);
526 }
527
528 SILC_CLIENT_CMD_REPLY_FUNC(oper)
529 {
530 }
531
532 /* Received reply for JOIN command. */
533
534 SILC_CLIENT_CMD_REPLY_FUNC(join)
535 {
536   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
537   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
538   SilcClient client = cmd->client;
539   SilcCommandStatus status;
540   unsigned int argc, mode;
541   unsigned char *id_string;
542   char *topic, *tmp, *channel_name;
543
544   SILC_LOG_DEBUG(("Start"));
545
546   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
547   SILC_GET16_MSB(status, tmp);
548   if (status != SILC_STATUS_OK) {
549     cmd->client->ops->say(cmd->client, conn,
550              "%s", silc_client_command_status_message(status));
551     COMMAND_REPLY_ERROR;
552     goto out;
553   }
554
555   argc = silc_command_get_arg_num(cmd->payload);
556   if (argc < 3 || argc > 4) {
557     cmd->client->ops->say(cmd->client, conn,
558              "Cannot join channel: Bad reply packet");
559     COMMAND_REPLY_ERROR;
560     goto out;
561   }
562
563   /* Get channel name */
564   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
565   if (!tmp) {
566     cmd->client->ops->say(cmd->client, conn, 
567                           "Cannot join channel: Bad reply packet");
568     COMMAND_REPLY_ERROR;
569     goto out;
570   }
571   channel_name = strdup(tmp);
572
573   /* Get Channel ID */
574   id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
575   if (!id_string) {
576     cmd->client->ops->say(cmd->client, conn, 
577                           "Cannot join channel: Bad reply packet");
578     COMMAND_REPLY_ERROR;
579     goto out;
580   }
581
582   /* Get channel mode */
583   tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
584   if (tmp)
585     SILC_GET32_MSB(mode, tmp);
586   else
587     mode = 0;
588
589   /* Get topic */
590   topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
591
592   /* Save received Channel ID */
593   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
594                              mode, id_string);
595
596   if (topic)
597     client->ops->say(cmd->client, conn, 
598                      "Topic for %s: %s", channel_name, topic);
599
600   /* Notify application */
601   COMMAND_REPLY((ARGS, channel_name, topic));
602
603  out:
604   silc_client_command_reply_free(cmd);
605 }
606
607 SILC_CLIENT_CMD_REPLY_FUNC(motd)
608 {
609 }
610
611 SILC_CLIENT_CMD_REPLY_FUNC(umode)
612 {
613 }
614
615 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
616 {
617 }
618
619 SILC_CLIENT_CMD_REPLY_FUNC(kick)
620 {
621 }
622
623 SILC_CLIENT_CMD_REPLY_FUNC(restart)
624 {
625 }
626  
627 SILC_CLIENT_CMD_REPLY_FUNC(close)
628 {
629 }
630  
631 SILC_CLIENT_CMD_REPLY_FUNC(die)
632 {
633 }
634  
635 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
636 {
637 }
638
639 /* Reply to LEAVE command. */
640 /* Sends to application: (no arguments) */
641
642 SILC_CLIENT_CMD_REPLY_FUNC(leave)
643 {
644   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
645   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
646   SilcCommandStatus status;
647   unsigned char *tmp;
648
649   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
650   SILC_GET16_MSB(status, tmp);
651   if (status != SILC_STATUS_OK) {
652     cmd->client->ops->say(cmd->client, conn,
653              "%s", silc_client_command_status_message(status));
654     COMMAND_REPLY_ERROR;
655     return;
656   }
657
658   /* Notify application */
659   COMMAND_REPLY((ARGS));
660
661   silc_client_command_reply_free(cmd);
662 }
663
664 /* Reply to NAMES command. Received list of client names on the channel 
665    we requested. */
666
667 SILC_CLIENT_CMD_REPLY_FUNC(names)
668 {
669   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
670   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
671   SilcCommandStatus status;
672   SilcIDCacheEntry id_cache = NULL;
673   SilcChannelEntry channel;
674   SilcChannelID *channel_id = NULL;
675   SilcBuffer client_id_list;
676   unsigned char *tmp;
677   char *name_list;
678   int i, len1, len2, list_count = 0;
679
680   SILC_LOG_DEBUG(("Start"));
681
682   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
683   SILC_GET16_MSB(status, tmp);
684   if (status != SILC_STATUS_OK) {
685     cmd->client->ops->say(cmd->client, conn,
686              "%s", silc_client_command_status_message(status));
687     COMMAND_REPLY_ERROR;
688     goto out;
689   }
690
691   /* Get channel ID */
692   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
693   if (!tmp) {
694     cmd->client->ops->say(cmd->client, conn, 
695                           "Cannot get user list: Bad reply packet");
696     COMMAND_REPLY_ERROR;
697     goto out;
698   }
699   channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
700
701   /* Get the name list of the channel */
702   name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
703   if (!name_list) {
704     cmd->client->ops->say(cmd->client, conn, 
705                           "Cannot get user list: Bad reply packet");
706     COMMAND_REPLY_ERROR;
707     goto out;
708   }
709
710   /* Get Client ID list */
711   tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
712   if (!tmp) {
713     cmd->client->ops->say(cmd->client, conn, 
714                           "Cannot get user list: Bad reply packet");
715     COMMAND_REPLY_ERROR;
716     goto out;
717   }
718
719   client_id_list = silc_buffer_alloc(len2);
720   silc_buffer_pull_tail(client_id_list, len2);
721   silc_buffer_put(client_id_list, tmp, len2);
722
723   /* Get the channel name */
724   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
725                                    SILC_ID_CHANNEL, &id_cache)) {
726     COMMAND_REPLY_ERROR;
727     goto out;
728   }
729   
730   channel = (SilcChannelEntry)id_cache->context;
731
732   /* If there is pending command we know that user has called this command
733      and we will handle the name list differently. */
734   if (cmd->callback) {
735     /* We will resolve all the necessary information about the people
736        on the channel. Only after that will we display the user list. */
737     for (i = 0; i < len1; i++) {
738       /* XXX */
739
740     }
741     silc_client_command_pending_del(SILC_COMMAND_NAMES);
742   } else {
743     /* there is no pending callback it means that this command reply
744        has been received without calling the command, ie. server has sent
745        the reply without getting the command from us first. This happens
746        with SILC servers that sends NAMES reply after joining to a channel. */
747
748     /* Remove commas from list */
749     for (i = 0; i < len1; i++)
750       if (name_list[i] == ',') {
751         name_list[i] = ' ';
752         list_count++;
753       }
754
755     cmd->client->ops->say(cmd->client, conn,
756                           "Users on %s: %s", channel->channel_name, name_list);
757   }
758
759   /* Cache the received name list and client ID's. This cache expires
760      whenever server sends notify message to channel. It means two things;
761      some user has joined or leaved the channel. */
762   for (i = 0; i < list_count; i++) {
763     int nick_len = strcspn(name_list, " ");
764     char *nickname = silc_calloc(nick_len, sizeof(*nickname));
765     SilcClientID *client_id;
766     SilcClientEntry client;
767
768     memcpy(nickname, name_list, nick_len);
769     client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
770     silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
771
772     client = silc_calloc(1, sizeof(*client));
773     client->id = client_id;
774     client->nickname = nickname;
775
776     silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
777                      client_id, (void *)client, TRUE);
778     name_list = name_list + nick_len + 1;
779   }
780
781   silc_buffer_free(client_id_list);
782
783  out:
784   if (channel_id)
785     silc_free(channel_id);
786   silc_client_command_reply_free(cmd);
787 }