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