Fixes to NAMES command handling.
[silc.git] / apps / silc / 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 /*
25  * $Id$
26  * $Log$
27  * Revision 1.5  2000/07/06 07:14:36  priikone
28  *      Fixes to NAMES command handling.
29  *      Fixes when leaving from channel.
30  *
31  * Revision 1.4  2000/07/05 06:12:34  priikone
32  *      Tweaked NAMES command reply for better. Should now show users
33  *      joined to a channel.
34  *
35  * Revision 1.3  2000/07/04 08:27:14  priikone
36  *      Changes to LEAVE command -- more consistent now and does error
37  *      handling better. Added INVITE, PING and part of NAMES commands.
38  *      SilcPacketContext is included into command structure.
39  *
40  * Revision 1.2  2000/07/03 05:49:49  priikone
41  *      Implemented LEAVE command.  Minor bug fixes.
42  *
43  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
44  *      Imported from internal CVS/Added Log headers.
45  *
46  *
47  */
48
49 #include "clientincludes.h"
50
51 /* Client command reply list. */
52 SilcClientCommandReply silc_command_reply_list[] =
53 {
54   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
55   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
56   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
57   SILC_CLIENT_CMD_REPLY(nick, NICK),
58   SILC_CLIENT_CMD_REPLY(list, LIST),
59   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
60   SILC_CLIENT_CMD_REPLY(invite, INVITE),
61   SILC_CLIENT_CMD_REPLY(quit, QUIT),
62   SILC_CLIENT_CMD_REPLY(kill, KILL),
63   SILC_CLIENT_CMD_REPLY(info, INFO),
64   SILC_CLIENT_CMD_REPLY(away, AWAY),
65   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
66   SILC_CLIENT_CMD_REPLY(ping, PING),
67   SILC_CLIENT_CMD_REPLY(oper, OPER),
68   SILC_CLIENT_CMD_REPLY(join, JOIN),
69   SILC_CLIENT_CMD_REPLY(motd, MOTD),
70   SILC_CLIENT_CMD_REPLY(umode, UMODE),
71   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
72   SILC_CLIENT_CMD_REPLY(kick, KICK),
73   SILC_CLIENT_CMD_REPLY(restart, RESTART),
74   SILC_CLIENT_CMD_REPLY(close, CLOSE),
75   SILC_CLIENT_CMD_REPLY(die, DIE),
76   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
77   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
78   SILC_CLIENT_CMD_REPLY(names, NAMES),
79
80   { NULL, 0 },
81 };
82
83 /* Status message structure. Messages are defined below. */
84 typedef struct {
85   SilcCommandStatus status;
86   char *message;
87 } SilcCommandStatusMessage;
88
89 /* Status messages returned by the server */
90 #define STAT(x) SILC_STATUS_ERR_##x
91 const SilcCommandStatusMessage silc_command_status_messages[] = {
92
93   { STAT(NO_SUCH_NICK),      "No such nickname" },
94   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
95   { STAT(NO_SUCH_SERVER),    "No such server" },
96   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
97   { STAT(NO_RECIPIENT),      "No recipient given" },
98   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
99   { STAT(WILDCARDS),         "Unknown command" },
100   { STAT(NO_CLIENT_ID),      "No Client ID given" },
101   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
102   { STAT(NO_SERVER_ID),      "No Server ID given" },
103   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
104   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
105   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
106   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
107   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
108   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
109   { STAT(USER_ON_CHANNEL),   "User already on channel" },
110   { STAT(NOT_REGISTERED),    "You have not registered" },
111   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
112   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
113   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
114   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
115   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
116   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
117   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
118   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
119   { STAT(UNKNOWN_MODE),    "Unknown mode" },
120   { STAT(NOT_YOU),         "Cannot change mode for other users" },
121   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
122   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
123   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
124   { STAT(BAD_NICKNAME),    "Bad nickname" },
125   { STAT(BAD_CHANNEL),     "Bad channel name" },
126   { STAT(AUTH_FAILED),     "Authentication failed" },
127
128   { 0, NULL }
129 };
130
131 /* Process received command reply. */
132
133 void silc_client_command_reply_process(SilcClient client,
134                                        SilcSocketConnection sock,
135                                        SilcPacketContext *packet)
136 {
137   SilcBuffer buffer = packet->buffer;
138   SilcClientCommandReplyContext ctx;
139   SilcCommandPayload payload;
140
141   /* Get command reply payload from packet */
142   payload = silc_command_parse_payload(buffer);
143   if (!payload) {
144     /* Silently ignore bad reply packet */
145     SILC_LOG_DEBUG(("Bad command reply packet"));
146     return;
147   }
148   
149   /* Allocate command reply context. This must be free'd by the
150      command reply routine receiving it. */
151   ctx = silc_calloc(1, sizeof(*ctx));
152   ctx->client = client;
153   ctx->sock = sock;
154   ctx->payload = payload;
155   ctx->packet = packet;
156       
157   /* Check for pending commands and mark to be exeucted */
158   SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
159   
160   /* Execute command reply */
161   SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
162 }
163
164 /* Returns status message string */
165
166 static char *
167 silc_client_command_status_message(SilcCommandStatus status)
168 {
169   int i;
170
171   for (i = 0; silc_command_status_messages[i].message; i++) {
172     if (silc_command_status_messages[i].status == status)
173       break;
174   }
175
176   if (silc_command_status_messages[i].message == NULL)
177     return NULL;
178
179   return silc_command_status_messages[i].message;
180 }
181
182 /* Free command reply context and its internals. */
183
184 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
185 {
186   if (cmd) {
187     silc_command_free_payload(cmd->payload);
188     silc_free(cmd);
189   }
190 }
191
192 /* Received reply for WHOIS command. This maybe called several times
193    for one WHOIS command as server may reply with list of results. */
194
195 SILC_CLIENT_CMD_REPLY_FUNC(whois)
196 {
197   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
198   SilcClient client = cmd->client;
199   SilcCommandStatus status;
200   unsigned char *tmp;
201
202   SILC_LOG_DEBUG(("Start"));
203
204   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
205   SILC_GET16_MSB(status, tmp);
206   if (status != SILC_STATUS_OK) {
207     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
208       /* Take nickname which may be provided */
209       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
210       if (tmp)
211         silc_say(cmd->client, "%s: %s", tmp,
212                  silc_client_command_status_message(status));
213       else
214         silc_say(cmd->client, "%s",
215                  silc_client_command_status_message(status));
216       goto out;
217     } else {
218       silc_say(cmd->client, "%s", silc_client_command_status_message(status));
219       goto out;
220     }
221   }
222
223   /* Display one whois reply */
224   if (status == SILC_STATUS_OK) {
225     char buf[256];
226     int argc, len;
227     unsigned char *id_data;
228     char *nickname = NULL, *username = NULL;
229     char *realname = NULL;
230     void *id;
231
232     memset(buf, 0, sizeof(buf));
233
234     argc = silc_command_get_arg_num(cmd->payload);
235     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
236
237     nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
238     if (nickname) {
239       strncat(buf, nickname, len);
240       strncat(buf, " is ", 4);
241     }
242
243     username = silc_command_get_arg_type(cmd->payload, 4, &len);
244     if (username) {
245       strncat(buf, username, len);
246     }
247
248     realname = silc_command_get_arg_type(cmd->payload, 5, &len);
249     if (realname) {
250       strncat(buf, " (", 2);
251       strncat(buf, realname, len);
252       strncat(buf, ")", 1);
253     }
254
255 #if 0
256     /* Save received Client ID to ID cache */
257     /* XXX Maybe should not be saved as /MSG will get confused */
258     id = silc_id_str2id(id_data, SILC_ID_CLIENT);
259     client->current_win->client_id_cache_count[(int)nickname[0] - 32] =
260     silc_idcache_add(&client->current_win->
261                      client_id_cache[(int)nickname[0] - 32],
262                      client->current_win->
263                      client_id_cache_count[(int)nickname[0] - 32],
264                      strdup(nickname), SILC_ID_CLIENT, id, NULL);
265 #endif
266
267     silc_say(cmd->client, "%s", buf);
268    }
269
270   if (status == SILC_STATUS_LIST_START) {
271
272   }
273
274   if (status == SILC_STATUS_LIST_END) {
275
276   }
277
278   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
279
280  out:
281   silc_client_command_reply_free(cmd);
282 }
283
284 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
285 {
286 }
287
288 /* Received reply for IDENTIFY command. This maybe called several times
289    for one IDENTIFY command as server may reply with list of results. 
290    This is totally silent and does not print anything on screen. */
291
292 SILC_CLIENT_CMD_REPLY_FUNC(identify)
293 {
294   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
295   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
296   SilcClientEntry client_entry;
297   SilcCommandStatus status;
298   unsigned char *tmp;
299
300   SILC_LOG_DEBUG(("Start"));
301
302 #define CIDC(x) win->client_id_cache[(x) - 32]
303 #define CIDCC(x) win->client_id_cache_count[(x) - 32]
304
305   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
306   SILC_GET16_MSB(status, tmp);
307   if (status != SILC_STATUS_OK) {
308     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
309       /* Take nickname which may be provided */
310       tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
311       if (tmp)
312         silc_say(cmd->client, "%s: %s", tmp,
313                  silc_client_command_status_message(status));
314       else
315         silc_say(cmd->client, "%s",
316                  silc_client_command_status_message(status));
317       goto out;
318     } else {
319       silc_say(cmd->client, "%s", silc_client_command_status_message(status));
320       goto out;
321     }
322   }
323
324   /* Display one whois reply */
325   if (status == SILC_STATUS_OK) {
326     unsigned char *id_data;
327     char *nickname;
328
329     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
330     nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
331
332     /* Allocate client entry */
333     client_entry = silc_calloc(1, sizeof(*client_entry));
334     client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
335     client_entry->nickname = strdup(nickname);
336
337     /* Save received Client ID to ID cache */
338     CIDCC(nickname[0]) =
339       silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]),
340                        client_entry->nickname, SILC_ID_CLIENT, 
341                        client_entry->id, client_entry);
342   }
343
344   if (status == SILC_STATUS_LIST_START) {
345
346   }
347
348   if (status == SILC_STATUS_LIST_END) {
349
350   }
351
352   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
353
354  out:
355   silc_client_command_reply_free(cmd);
356 #undef CIDC
357 #undef CIDCC
358 }
359
360 /* Received reply for command NICK. If everything went without errors
361    we just received our new Client ID. */
362
363 SILC_CLIENT_CMD_REPLY_FUNC(nick)
364 {
365   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
366   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
367   SilcCommandStatus status;
368   unsigned char *tmp, *id_string;
369   int argc;
370
371   SILC_LOG_DEBUG(("Start"));
372
373   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
374   SILC_GET16_MSB(status, tmp);
375   if (status != SILC_STATUS_OK) {
376     silc_say(cmd->client, "Cannot set nickname: %s", 
377              silc_client_command_status_message(status));
378     goto out;
379   }
380
381   argc = silc_command_get_arg_num(cmd->payload);
382   if (argc < 2 || argc > 2) {
383     silc_say(cmd->client, "Cannot set nickname: bad reply to command");
384     goto out;
385   }
386
387   /* Take received Client ID */
388   id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
389   silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
390
391   /* Update nickname on screen */
392   cmd->client->screen->bottom_line->nickname = win->nickname;
393   silc_screen_print_bottom_line(cmd->client->screen, 0);
394
395  out:
396   silc_client_command_reply_free(cmd);
397 }
398
399 SILC_CLIENT_CMD_REPLY_FUNC(list)
400 {
401 }
402
403 SILC_CLIENT_CMD_REPLY_FUNC(topic)
404 {
405 }
406
407 /* Received reply to invite command. */
408
409 SILC_CLIENT_CMD_REPLY_FUNC(invite)
410 {
411   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
412   SilcCommandStatus status;
413   unsigned char *tmp;
414
415   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
416   SILC_GET16_MSB(status, tmp);
417   if (status != SILC_STATUS_OK) {
418     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
419     silc_client_command_reply_free(cmd);
420     return;
421   }
422
423   silc_client_command_reply_free(cmd);
424 }
425  
426 SILC_CLIENT_CMD_REPLY_FUNC(quit)
427 {
428 }
429
430 SILC_CLIENT_CMD_REPLY_FUNC(kill)
431 {
432 }
433
434 SILC_CLIENT_CMD_REPLY_FUNC(info)
435 {
436 }
437
438 SILC_CLIENT_CMD_REPLY_FUNC(away)
439 {
440 }
441
442 SILC_CLIENT_CMD_REPLY_FUNC(connect)
443 {
444 }
445
446 /* Received reply to PING command. The reply time is shown to user. */
447
448 SILC_CLIENT_CMD_REPLY_FUNC(ping)
449 {
450   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
451   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
452   SilcCommandStatus status;
453   void *id;
454   char *tmp;
455   int i;
456   time_t diff, curtime;
457
458   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
459   SILC_GET16_MSB(status, tmp);
460   if (status != SILC_STATUS_OK) {
461     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
462     goto out;
463   }
464
465   curtime = time(NULL);
466   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
467
468   for (i = 0; i < win->ping_count; i++) {
469     if (!SILC_ID_SERVER_COMPARE(win->ping[i].dest_id, id)) {
470       diff = curtime - win->ping[i].start_time;
471       silc_say(cmd->client, "Ping reply from %s: %d second%s", 
472                win->ping[i].dest_name, diff, diff == 1 ? "" : "s");
473
474       win->ping[i].start_time = 0;
475       silc_free(win->ping[i].dest_id);
476       win->ping[i].dest_id = NULL;
477       silc_free(win->ping[i].dest_name);
478       win->ping[i].dest_name = NULL;
479       goto out;
480     }
481   }
482
483  out:
484   silc_client_command_reply_free(cmd);
485 }
486
487 SILC_CLIENT_CMD_REPLY_FUNC(oper)
488 {
489 }
490
491 /* Received reply for JOIN command. */
492
493 SILC_CLIENT_CMD_REPLY_FUNC(join)
494 {
495   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
496   SilcClient client = cmd->client;
497   SilcCommandStatus status;
498   unsigned int argc, mode;
499   unsigned char *id_string;
500   char *topic, *tmp, *channel_name;
501
502   SILC_LOG_DEBUG(("Start"));
503
504   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
505   SILC_GET16_MSB(status, tmp);
506   if (status != SILC_STATUS_OK) {
507     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
508     goto out;
509   }
510
511   argc = silc_command_get_arg_num(cmd->payload);
512   if (argc < 3 || argc > 4) {
513     silc_say(cmd->client, "Cannot join channel: Bad reply packet");
514     goto out;
515   }
516
517   /* Get channel name */
518   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
519   if (!tmp) {
520     silc_say(cmd->client, "Cannot join channel: Bad reply packet");
521     goto out;
522   }
523   channel_name = strdup(tmp);
524
525   /* Get Channel ID */
526   id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
527   if (!id_string) {
528     silc_say(cmd->client, "Cannot join channel: Bad reply packet");
529     goto out;
530   }
531
532   /* Get channel mode */
533   tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
534   if (tmp)
535     SILC_GET32_MSB(mode, tmp);
536   else
537     mode = 0;
538
539   /* Get topic */
540   topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
541
542   /* Save received Channel ID */
543   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
544                              mode, id_string);
545
546   /* Print channel name on screen */
547   client->screen->bottom_line->channel = channel_name;
548   silc_screen_print_bottom_line(client->screen, 0);
549
550   if (topic)
551     silc_say(client, "Topic for %s: %s", channel_name, topic);
552
553  out:
554   silc_client_command_reply_free(cmd);
555 }
556
557 SILC_CLIENT_CMD_REPLY_FUNC(motd)
558 {
559 }
560
561 SILC_CLIENT_CMD_REPLY_FUNC(umode)
562 {
563 }
564
565 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
566 {
567 }
568
569 SILC_CLIENT_CMD_REPLY_FUNC(kick)
570 {
571 }
572
573 SILC_CLIENT_CMD_REPLY_FUNC(restart)
574 {
575 }
576  
577 SILC_CLIENT_CMD_REPLY_FUNC(close)
578 {
579 }
580  
581 SILC_CLIENT_CMD_REPLY_FUNC(die)
582 {
583 }
584  
585 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
586 {
587 }
588
589 /* Reply to LEAVE command. */
590
591 SILC_CLIENT_CMD_REPLY_FUNC(leave)
592 {
593   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
594   SilcClient client = cmd->client;
595   SilcCommandStatus status;
596   unsigned char *tmp;
597
598   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
599   SILC_GET16_MSB(status, tmp);
600   if (status != SILC_STATUS_OK)
601     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
602
603   silc_client_command_reply_free(cmd);
604 }
605
606 /* Reply to NAMES command. Received list of client names on the channel 
607    we requested. */
608
609 SILC_CLIENT_CMD_REPLY_FUNC(names)
610 {
611   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
612   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
613   SilcCommandStatus status;
614   SilcIDCache *id_cache = NULL;
615   SilcChannelEntry channel;
616   SilcChannelID *channel_id = NULL;
617   SilcBuffer client_id_list;
618   unsigned char *tmp;
619   char *name_list;
620   int i, len1, len2, list_count = 0;
621
622   SILC_LOG_DEBUG(("Start"));
623
624 #define CIDC(x) win->channel_id_cache[(x)]
625 #define CIDCC(x) win->channel_id_cache_count[(x)]
626 #define CLC(x) win->client_id_cache[(x) - 32]
627 #define CLCC(x) win->client_id_cache_count[(x) - 32]
628
629   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
630   SILC_GET16_MSB(status, tmp);
631   if (status != SILC_STATUS_OK) {
632     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
633     goto out;
634   }
635
636   /* Get channel ID */
637   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
638   if (!tmp) {
639     silc_say(cmd->client, "Cannot get user list: Bad reply packet");
640     goto out;
641   }
642   channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
643
644   /* Get the name list of the channel */
645   name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
646   if (!name_list) {
647     silc_say(cmd->client, "Cannot get user list: Bad reply packet");
648     goto out;
649   }
650
651   /* Get Client ID list */
652   tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
653   if (!tmp) {
654     silc_say(cmd->client, "Cannot get user list: Bad reply packet");
655     goto out;
656   }
657
658   client_id_list = silc_buffer_alloc(len2);
659   silc_buffer_pull_tail(client_id_list, len2);
660   silc_buffer_put(client_id_list, tmp, len2);
661
662   /* Get the channel name */
663   for (i = 0; i < 96; i++) {
664     if (CIDC(i) == NULL)
665       continue;
666     if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)channel_id, 
667                                 SILC_ID_CHANNEL, &id_cache))
668       break;
669   }
670   if (!id_cache)
671     goto out;
672   
673   channel = (SilcChannelEntry)id_cache->context;
674
675   /* If there is pending command we know that user has called this command
676      and we will handle the name list differently. */
677   if (cmd->callback) {
678     /* We will resolve all the necessary information about the people
679        on the channel. Only after that will we display the user list. */
680     for (i = 0; i < len1; i++) {
681       /* XXX */
682
683     }
684     silc_client_command_pending_del(SILC_COMMAND_NAMES);
685   } else {
686     /* there is no pending callback it means that this command reply
687        has been received without calling the command, ie. server has sent
688        the reply without getting the command from us first. This happens
689        with SILC servers that sends NAMES reply after joining to a channel. */
690
691     /* Remove commas from list */
692     for (i = 0; i < len1; i++)
693       if (name_list[i] == ',') {
694         name_list[i] = ' ';
695         list_count++;
696       }
697
698     silc_say(cmd->client, "Users on %s: %s", channel->channel_name, name_list);
699   }
700
701   /* Cache the received name list and client ID's. This cache expires
702      whenever server sends notify message to channel. It means to things;
703      some user has joined or leaved the channel. */
704   for (i = 0; i < list_count; i++) {
705     int nick_len = strcspn(name_list, " ");
706     char *nickname = silc_calloc(nick_len, sizeof(*nickname));
707     SilcClientID *client_id;
708     SilcClientEntry client;
709
710     memcpy(nickname, name_list, nick_len);
711     client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT_LEN);
712     silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
713
714     client = silc_calloc(1, sizeof(*client));
715     client->id = client_id;
716     client->nickname = nickname;
717
718     CLCC(nickname[0]) = silc_idcache_add(&CLC(nickname[0]), CLCC(nickname[0]),
719                                          nickname, SILC_ID_CLIENT, 
720                                          client_id, (void *)client);
721
722     name_list = name_list + nick_len + 1;
723   }
724
725   silc_buffer_free(client_id_list);
726
727  out:
728   if (channel_id)
729     silc_free(channel_id);
730   silc_client_command_reply_free(cmd);
731 #undef CIDC
732 #undef CIDCC
733 #undef CLC
734 #undef CLCC
735 }
736
737 /* Private message received. This processes the private message and
738    finally displays it on the screen. */
739
740 SILC_CLIENT_CMD_REPLY_FUNC(msg)
741 {
742   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
743   SilcClient client = cmd->client;
744   SilcBuffer buffer = (SilcBuffer)cmd->context;
745   unsigned short nick_len;
746   unsigned char *nickname, *message;
747   SilcIDCache *id_cache;
748   unsigned char *id_string;
749   void *id;
750
751   /* Get nickname */
752   silc_buffer_unformat(buffer, 
753                        SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
754                        SILC_STR_END);
755   silc_buffer_pull(buffer, 2 + nick_len);
756
757 #if 0
758   /* Get ID of the sender */
759   id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *));
760   silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
761   memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN);
762   silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
763   id = silc_id_str2id(id_string, SILC_ID_CLIENT);
764   silc_free(id_string);
765
766   /* Nickname should be verified if we don't have it in the cache */
767   if (silc_idcache_find_by_data(client->current_win->
768                                 client_id_cache[nickname[0] - 32],
769                                 client->current_win->
770                                 client_id_cache_count[nickname[0] - 32],
771                                 nickname, &id_cache) == FALSE) {
772
773     SilcClientCommandContext ctx;
774     char whois[255];
775
776     /* Private message from unknown source, try to resolve it. */
777
778
779     return;
780   }
781 #endif
782      
783   message = silc_calloc(buffer->len + 1, sizeof(char));
784   memcpy(message, buffer->data, buffer->len);
785   silc_print(client, "*%s* %s", nickname, message);
786   memset(message, 0, buffer->len);
787   silc_free(message);
788 }