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