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