Created SILC Client Libary by moving stuff from silc/ directory.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.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 /* $Id$ */
21
22 #include "clientlibincludes.h"
23
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
26 {
27   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
28   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
30                   SILC_CF_LAG | SILC_CF_REG, 3),
31   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
32   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
34   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
36   SILC_CLIENT_CMD(kill, KILL, "KILL", 
37                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
41   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42   SILC_CLIENT_CMD(oper, OPER, "OPER",
43                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
45   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
49   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
50                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
51   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
52                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53   SILC_CLIENT_CMD(die, DIE, "DIE",
54                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
55   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
57   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
58   SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
59
60   { NULL, 0, NULL, 0, 0 },
61 };
62
63 #define SILC_NOT_CONNECTED(x, c) \
64   x->ops->say((x), (c), \
65            "You are not connected to a server, use /SERVER to connect");
66
67 /* Command operation that is called at the end of all commands. 
68    Usage: COMMAND; */
69 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
70   cmd, TRUE, cmd->command->cmd)
71
72 /* Error to application. Usage: COMMAND_ERROR; */
73 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
74   cmd, FALSE, cmd->command->cmd)
75
76 /* List of pending commands. */
77 SilcClientCommandPending *silc_command_pending = NULL;
78
79 /* Finds and returns a pointer to the command list. Return NULL if the
80    command is not found. */
81
82 SilcClientCommand *silc_client_command_find(const char *name)
83 {
84   SilcClientCommand *cmd;
85
86   for (cmd = silc_command_list; cmd->name; cmd++) {
87     if (!strcmp(cmd->name, name))
88       return cmd;
89   }
90
91   return NULL;
92 }
93
94 /* Add new pending command to the list of pending commands. Currently
95    pending commands are executed from command replies, thus we can
96    execute any command after receiving some specific command reply.
97
98    The argument `reply_cmd' is the command reply from where the callback
99    function is to be called, thus, it IS NOT the command to be executed.
100
101    XXX: If needed in the future this support may be extended for
102    commands as well, when any command could be executed after executing
103    some specific command. */
104
105 void silc_client_command_pending(SilcCommand reply_cmd,
106                                  SilcClientCommandCallback callback,
107                                  void *context)
108 {
109   SilcClientCommandPending *reply, *r;
110
111   reply = silc_calloc(1, sizeof(*reply));
112   reply->reply_cmd = reply_cmd;
113   reply->context = context;
114   reply->callback = callback;
115
116   if (silc_command_pending == NULL) {
117     silc_command_pending = reply;
118     return;
119   }
120
121   for (r = silc_command_pending; r; r = r->next) {
122     if (r->next == NULL) {
123       r->next = reply;
124       break;
125     }
126   }
127 }
128
129 /* Deletes pending command by reply command type. */
130
131 void silc_client_command_pending_del(SilcCommand reply_cmd)
132 {
133   SilcClientCommandPending *r, *tmp;
134   
135   if (silc_command_pending) {
136     if (silc_command_pending->reply_cmd == reply_cmd) {
137       silc_free(silc_command_pending);
138       silc_command_pending = NULL;
139       return;
140     }
141
142     for (r = silc_command_pending; r; r = r->next) {
143       if (r->next && r->next->reply_cmd == reply_cmd) {
144         tmp = r->next;
145         r->next = r->next->next;
146         silc_free(tmp);
147         break;
148       }
149     }
150   }
151 }
152
153 /* Free command context and its internals */
154
155 void silc_client_command_free(SilcClientCommandContext cmd)
156 {
157   int i;
158
159   if (cmd) {
160     for (i = 0; i < cmd->argc; i++)
161       silc_free(cmd->argv[i]);
162     silc_free(cmd);
163   }
164 }
165
166 /* Command WHOIS. This command is used to query information about 
167    specific user. */
168
169 SILC_CLIENT_CMD_FUNC(whois)
170 {
171   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
172   SilcClientConnection conn = cmd->conn;
173   SilcBuffer buffer;
174
175   if (!cmd->conn) {
176     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
177     COMMAND_ERROR;
178     goto out;
179   }
180
181   if (cmd->argc < 2 || cmd->argc > 3) {
182     cmd->client->ops->say(cmd->client, conn, 
183              "Usage: /WHOIS <nickname>[@<server>] [<count>]");
184     COMMAND_ERROR;
185     goto out;
186   }
187
188   buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
189                                        cmd->argc - 1, ++cmd->argv,
190                                        ++cmd->argv_lens, ++cmd->argv_types);
191   silc_client_packet_send(cmd->client, cmd->conn->sock,
192                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
193                           buffer->data, buffer->len, TRUE);
194   silc_buffer_free(buffer);
195   cmd->argv--;
196   cmd->argv_lens--;
197   cmd->argv_types--;
198
199   /* Notify application */
200   COMMAND;
201
202  out:
203   silc_client_command_free(cmd);
204 }
205
206 SILC_CLIENT_CMD_FUNC(whowas)
207 {
208 }
209
210 /* Command IDENTIFY. This command is used to query information about 
211    specific user, especially ID's. */
212
213 SILC_CLIENT_CMD_FUNC(identify)
214 {
215   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
216   SilcClientConnection conn = cmd->conn;
217   SilcBuffer buffer;
218
219   if (!cmd->conn) {
220     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
221     COMMAND_ERROR;
222     goto out;
223   }
224
225   if (cmd->argc < 2 || cmd->argc > 3) {
226     cmd->client->ops->say(cmd->client, conn,
227              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
228     COMMAND_ERROR;
229     goto out;
230   }
231
232   buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
233                                        cmd->argc - 1, ++cmd->argv,
234                                        ++cmd->argv_lens, ++cmd->argv_types);
235   silc_client_packet_send(cmd->client, cmd->conn->sock,
236                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
237                           buffer->data, buffer->len, TRUE);
238   silc_buffer_free(buffer);
239   cmd->argv--;
240   cmd->argv_lens--;
241   cmd->argv_types--;
242
243   /* Notify application */
244   COMMAND;
245
246  out:
247   silc_client_command_free(cmd);
248 }
249
250 /* Command NICK. Shows current nickname/sets new nickname on current
251    window. */
252
253 SILC_CLIENT_CMD_FUNC(nick)
254 {
255   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
256   SilcClientConnection conn = cmd->conn;
257   SilcBuffer buffer;
258
259   if (!cmd->conn) {
260     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
261     COMMAND_ERROR;
262     goto out;
263   }
264
265   /* Show current nickname */
266   if (cmd->argc < 2) {
267     if (cmd->conn) {
268       cmd->client->ops->say(cmd->client, conn, 
269                             "Your nickname is %s on server %s", 
270                             conn->nickname, conn->remote_host);
271     } else {
272       cmd->client->ops->say(cmd->client, conn, 
273                             "Your nickname is %s", conn->nickname);
274     }
275     /* XXX Notify application */
276     COMMAND;
277     goto out;
278   }
279
280   /* Set new nickname */
281   buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
282                                        cmd->argc - 1, ++cmd->argv,
283                                        ++cmd->argv_lens, ++cmd->argv_types);
284   silc_client_packet_send(cmd->client, cmd->conn->sock,
285                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
286                           buffer->data, buffer->len, TRUE);
287   silc_buffer_free(buffer);
288   cmd->argv--;
289   cmd->argv_lens--;
290   cmd->argv_types--;
291   if (conn->nickname)
292     silc_free(conn->nickname);
293   conn->nickname = strdup(cmd->argv[1]);
294
295   /* Notify application */
296   COMMAND;
297
298  out:
299   silc_client_command_free(cmd);
300 }
301
302 SILC_CLIENT_CMD_FUNC(list)
303 {
304 }
305
306 SILC_CLIENT_CMD_FUNC(topic)
307 {
308 }
309
310 /* Command INVITE. Invites specific client to join a channel. */
311
312 SILC_CLIENT_CMD_FUNC(invite)
313 {
314   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
315   SilcClient client = cmd->client;
316   SilcClientConnection conn = cmd->conn;
317   SilcClientEntry client_entry;
318   SilcChannelEntry channel_entry;
319   SilcBuffer buffer;
320   unsigned int num = 0;
321   char *nickname = NULL, *server = NULL;
322   unsigned char *client_id, *channel_id;
323
324   if (!cmd->conn) {
325     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
326     COMMAND_ERROR;
327     goto out;
328   }
329
330   if (cmd->argc != 3) {
331     cmd->client->ops->say(cmd->client, conn,
332                           "Usage: /INVITE <nickname>[@<server>] <channel>");
333     COMMAND_ERROR;
334     goto out;
335   }
336
337   /* Parse the typed nickname. */
338   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
339     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
340     COMMAND_ERROR;
341     goto out;
342   }
343
344   /* Find client entry */
345   client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
346   if (!client_entry) {
347     /* Client entry not found, it was requested thus mark this to be
348        pending command. */
349     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
350                                 silc_client_command_invite, context);
351     return;
352   }
353   
354   client_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
355
356   /* Find channel entry */
357   channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
358   if (!channel_entry) {
359     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
360     silc_free(client_id);
361     COMMAND_ERROR;
362     goto out;
363   }
364
365   channel_id = silc_id_id2str(channel_entry->id, SILC_ID_CHANNEL);
366
367   buffer = silc_command_encode_payload_va(SILC_COMMAND_INVITE, 2,
368                                           1, client_id, SILC_ID_CLIENT_LEN,
369                                           2, channel_id, SILC_ID_CHANNEL_LEN);
370   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
371                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
372   silc_buffer_free(buffer);
373
374   cmd->client->ops->say(cmd->client, conn, 
375                         "Inviting %s to channel %s", cmd->argv[1], 
376                         cmd->argv[2]);
377
378   /* Notify application */
379   COMMAND;
380
381  out:
382   silc_client_command_free(cmd);
383 }
384
385 /* Command QUIT. Closes connection with current server. */
386  
387 SILC_CLIENT_CMD_FUNC(quit)
388 {
389   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
390   SilcBuffer buffer;
391
392   if (!cmd->conn) {
393     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
394     COMMAND_ERROR;
395     goto out;
396   }
397
398   buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
399                                        ++cmd->argv, ++cmd->argv_lens,
400                                        ++cmd->argv_types);
401   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
402                           NULL, 0, NULL, NULL, 
403                           buffer->data, buffer->len, TRUE);
404   silc_buffer_free(buffer);
405   cmd->argv--;
406   cmd->argv_lens--;
407   cmd->argv_types--;
408
409   /* Close connection */
410   silc_client_close_connection(cmd->client, cmd->conn->sock);
411   cmd->client->ops->disconnect(cmd->client, cmd->conn);
412
413   /* Notify application */
414   COMMAND;
415
416  out:
417   silc_client_command_free(cmd);
418 }
419
420 SILC_CLIENT_CMD_FUNC(kill)
421 {
422 }
423
424 /* Command INFO. Request information about specific server. If specific
425    server is not provided the current server is used. */
426
427 SILC_CLIENT_CMD_FUNC(info)
428 {
429   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
430   SilcClientConnection conn = cmd->conn;
431   SilcBuffer buffer;
432   char *name;
433
434   if (!cmd->conn) {
435     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
436     COMMAND_ERROR;
437     goto out;
438   }
439
440   if (cmd->argc < 2)
441     name = strdup(conn->remote_host);
442   else
443     name = strdup(cmd->argv[1]);
444
445   /* Send the command */
446   buffer = silc_command_encode_payload_va(SILC_COMMAND_INFO, 1, 
447                                           1, name, strlen(name));
448   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
449                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
450   silc_buffer_free(buffer);
451
452   /* Notify application */
453   COMMAND;
454
455  out:
456   silc_client_command_free(cmd);
457 }
458
459 SILC_CLIENT_CMD_FUNC(connect)
460 {
461 }
462
463 /* Command PING. Sends ping to server. This is used to test the 
464    communication channel. */
465
466 SILC_CLIENT_CMD_FUNC(ping)
467 {
468   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
469   SilcClientConnection conn = cmd->conn;
470   SilcBuffer buffer;
471   void *id;
472   int i;
473   char *name = NULL;
474
475   if (!cmd->conn) {
476     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
477     COMMAND_ERROR;
478     goto out;
479   }
480
481   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
482     name = strdup(conn->remote_host);
483
484   id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
485
486   /* Send the command */
487   buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1, 
488                                           1, conn->remote_id_data, 
489                                           SILC_ID_SERVER_LEN);
490   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
491                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
492   silc_buffer_free(buffer);
493
494   /* Start counting time */
495   for (i = 0; i < conn->ping_count; i++) {
496     if (conn->ping[i].dest_id == NULL) {
497       conn->ping[i].start_time = time(NULL);
498       conn->ping[i].dest_id = id;
499       conn->ping[i].dest_name = name;
500       conn->ping_count++;
501       break;
502     }
503   }
504   if (i >= conn->ping_count) {
505     i = conn->ping_count;
506     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
507     conn->ping[i].start_time = time(NULL);
508     conn->ping[i].dest_id = id;
509     conn->ping[i].dest_name = name;
510     conn->ping_count++;
511   }
512   
513   /* Notify application */
514   COMMAND;
515
516  out:
517   silc_client_command_free(cmd);
518 }
519
520 SILC_CLIENT_CMD_FUNC(oper)
521 {
522 }
523
524 SILC_CLIENT_CMD_FUNC(trace)
525 {
526 }
527
528 SILC_CLIENT_CMD_FUNC(notice)
529 {
530 }
531
532 /* Command JOIN. Joins to a channel. */
533
534 SILC_CLIENT_CMD_FUNC(join)
535 {
536   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
537   SilcClientConnection conn = cmd->conn;
538   SilcIDCacheEntry id_cache = NULL;
539   SilcBuffer buffer;
540
541   if (!cmd->conn) {
542     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
543     COMMAND_ERROR;
544     goto out;
545   }
546
547   if (cmd->argc < 2) {
548     /* Show channels currently joined to */
549
550     goto out;
551   }
552
553   /* See if we have joined to the requested channel already */
554   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
555                                     &id_cache)) {
556     cmd->client->ops->say(cmd->client, conn, 
557                           "You are talking to channel %s", cmd->argv[1]);
558     conn->current_channel = (SilcChannelEntry)id_cache->context;
559 #if 0
560     cmd->client->screen->bottom_line->channel = cmd->argv[1];
561     silc_screen_print_bottom_line(cmd->client->screen, 0);
562 #endif
563     goto out;
564   }
565
566   /* Send JOIN command to the server */
567   buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
568                                        cmd->argc - 1, ++cmd->argv,
569                                        ++cmd->argv_lens, ++cmd->argv_types);
570   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
571                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
572   silc_buffer_free(buffer);
573   cmd->argv--;
574   cmd->argv_lens--;
575   cmd->argv_types--;
576
577   /* Notify application */
578   COMMAND;
579
580  out:
581   silc_client_command_free(cmd);
582 }
583
584 SILC_CLIENT_CMD_FUNC(motd)
585 {
586 }
587
588 SILC_CLIENT_CMD_FUNC(umode)
589 {
590 }
591
592 SILC_CLIENT_CMD_FUNC(cmode)
593 {
594 }
595
596 SILC_CLIENT_CMD_FUNC(kick)
597 {
598 }
599
600 SILC_CLIENT_CMD_FUNC(restart)
601 {
602 }
603  
604 SILC_CLIENT_CMD_FUNC(close)
605 {
606 }
607  
608 SILC_CLIENT_CMD_FUNC(die)
609 {
610 }
611  
612 SILC_CLIENT_CMD_FUNC(silcoper)
613 {
614 }
615
616 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
617
618 SILC_CLIENT_CMD_FUNC(leave)
619 {
620   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
621   SilcClientConnection conn = cmd->conn;
622   SilcIDCacheEntry id_cache = NULL;
623   SilcChannelEntry channel;
624   SilcBuffer buffer;
625   unsigned char *id_string;
626   char *name;
627
628   if (!cmd->conn) {
629     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
630     COMMAND_ERROR;
631     goto out;
632   }
633
634   if (cmd->argc != 2) {
635     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
636     COMMAND_ERROR;
637     goto out;
638   }
639
640   if (cmd->argv[1][0] == '*') {
641     if (!conn->current_channel) {
642       cmd->client->ops->say(cmd->client, conn, "You are not on any chanenl");
643       COMMAND_ERROR;
644       goto out;
645     }
646     name = conn->current_channel->channel_name;
647   } else {
648     name = cmd->argv[1];
649   }
650
651   if (!conn->current_channel) {
652     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
653     COMMAND_ERROR;
654     goto out;
655   }
656
657   /* Get the Channel ID of the channel */
658   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
659     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
660     COMMAND_ERROR;
661     goto out;
662   }
663
664   channel = (SilcChannelEntry)id_cache->context;
665
666   /* Send LEAVE command to the server */
667   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
668   buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1, 
669                                           1, id_string, SILC_ID_CHANNEL_LEN);
670   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
671                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
672   silc_buffer_free(buffer);
673
674   /* We won't talk anymore on this channel */
675   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
676
677   conn->current_channel = NULL;
678
679   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
680   silc_free(channel->channel_name);
681   silc_free(channel->id);
682   silc_free(channel->key);
683   silc_cipher_free(channel->channel_key);
684   silc_free(channel);
685   silc_free(id_string);
686
687   /* Notify application */
688   COMMAND;
689
690  out:
691   silc_client_command_free(cmd);
692 }
693
694 /* Command NAMES. Requests the names of the clients joined on requested
695    channel. */
696
697 SILC_CLIENT_CMD_FUNC(names)
698 {
699   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
700   SilcClientConnection conn = cmd->conn;
701   SilcIDCacheEntry id_cache = NULL;
702   SilcBuffer buffer;
703   char *name;
704   unsigned char *id_string;
705
706   if (!cmd->conn) {
707     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
708     COMMAND_ERROR;
709     goto out;
710   }
711
712   if (cmd->argc != 2) {
713     cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
714     COMMAND_ERROR;
715     goto out;
716   }
717
718   if (cmd->argv[1][0] == '*')
719     name = conn->current_channel->channel_name;
720   else
721     name = cmd->argv[1];
722
723   /* Get the Channel ID of the channel */
724   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
725     /* XXX should resolve the channel ID; LIST command */
726     cmd->client->ops->say(cmd->client, conn, 
727                           "You are not on that channel", name);
728     COMMAND_ERROR;
729     goto out;
730   }
731
732   /* Send NAMES command to the server */
733   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
734   buffer = silc_command_encode_payload_va(SILC_COMMAND_NAMES, 1, 
735                                           1, id_string, SILC_ID_CHANNEL_LEN);
736   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
737                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
738   silc_buffer_free(buffer);
739   silc_free(id_string);
740
741   /* Register dummy pending command that will tell the reply command
742      that user called this command. Server may send reply to this command
743      even if user did not send this command thus we want to handle things
744      differently when user sent the command. This is dummy and won't be
745      execute. */
746   /* XXX this is kludge and should be removed after pending command reply 
747      support is added. Currently only commands may be pending not command
748      replies. */
749   silc_client_command_pending(SILC_COMMAND_NAMES, 
750                               silc_client_command_names, NULL);
751
752   /* Notify application */
753   COMMAND;
754
755  out:
756   silc_client_command_free(cmd);
757 }