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