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