Implemented new notify payload handling. Also changed notify
[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, 3),
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_payload_encode(SILC_COMMAND_WHOIS,
189                                        cmd->argc - 1, ++cmd->argv,
190                                        ++cmd->argv_lens, ++cmd->argv_types,
191                                        0);
192   silc_client_packet_send(cmd->client, cmd->conn->sock,
193                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
194                           buffer->data, buffer->len, TRUE);
195   silc_buffer_free(buffer);
196   cmd->argv--;
197   cmd->argv_lens--;
198   cmd->argv_types--;
199
200   /* Notify application */
201   COMMAND;
202
203  out:
204   silc_client_command_free(cmd);
205 }
206
207 SILC_CLIENT_CMD_FUNC(whowas)
208 {
209 }
210
211 /* Command IDENTIFY. This command is used to query information about 
212    specific user, especially ID's. */
213
214 SILC_CLIENT_CMD_FUNC(identify)
215 {
216   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
217   SilcClientConnection conn = cmd->conn;
218   SilcBuffer buffer;
219
220   if (!cmd->conn) {
221     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
222     COMMAND_ERROR;
223     goto out;
224   }
225
226   if (cmd->argc < 2 || cmd->argc > 3) {
227     cmd->client->ops->say(cmd->client, conn,
228              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
229     COMMAND_ERROR;
230     goto out;
231   }
232
233   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
234                                        cmd->argc - 1, ++cmd->argv,
235                                        ++cmd->argv_lens, ++cmd->argv_types,
236                                        0);
237   silc_client_packet_send(cmd->client, cmd->conn->sock,
238                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
239                           buffer->data, buffer->len, TRUE);
240   silc_buffer_free(buffer);
241   cmd->argv--;
242   cmd->argv_lens--;
243   cmd->argv_types--;
244
245   /* Notify application */
246   COMMAND;
247
248  out:
249   silc_client_command_free(cmd);
250 }
251
252 /* Command NICK. Shows current nickname/sets new nickname on current
253    window. */
254
255 SILC_CLIENT_CMD_FUNC(nick)
256 {
257   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
258   SilcClientConnection conn = cmd->conn;
259   SilcBuffer buffer;
260
261   if (!cmd->conn) {
262     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
263     COMMAND_ERROR;
264     goto out;
265   }
266
267   /* Show current nickname */
268   if (cmd->argc < 2) {
269     if (cmd->conn) {
270       cmd->client->ops->say(cmd->client, conn, 
271                             "Your nickname is %s on server %s", 
272                             conn->nickname, conn->remote_host);
273     } else {
274       cmd->client->ops->say(cmd->client, conn, 
275                             "Your nickname is %s", conn->nickname);
276     }
277     /* XXX Notify application */
278     COMMAND;
279     goto out;
280   }
281
282   /* Set new nickname */
283   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
284                                        cmd->argc - 1, ++cmd->argv,
285                                        ++cmd->argv_lens, ++cmd->argv_types,
286                                        0);
287   silc_client_packet_send(cmd->client, cmd->conn->sock,
288                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
289                           buffer->data, buffer->len, TRUE);
290   silc_buffer_free(buffer);
291   cmd->argv--;
292   cmd->argv_lens--;
293   cmd->argv_types--;
294   if (conn->nickname)
295     silc_free(conn->nickname);
296   conn->nickname = strdup(cmd->argv[1]);
297
298   /* Notify application */
299   COMMAND;
300
301  out:
302   silc_client_command_free(cmd);
303 }
304
305 SILC_CLIENT_CMD_FUNC(list)
306 {
307 }
308
309 /* Command TOPIC. Sets/shows topic on a channel. */
310
311 SILC_CLIENT_CMD_FUNC(topic)
312 {
313   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
314   SilcClientConnection conn = cmd->conn;
315   SilcIDCacheEntry id_cache = NULL;
316   SilcChannelEntry channel;
317   SilcBuffer buffer;
318   unsigned char *id_string;
319   char *name;
320
321   if (!cmd->conn) {
322     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
323     COMMAND_ERROR;
324     goto out;
325   }
326
327   if (cmd->argc < 2 || cmd->argc > 3) {
328     cmd->client->ops->say(cmd->client, conn,
329                           "Usage: /TOPIC <channel> [<topic>]");
330     COMMAND_ERROR;
331     goto out;
332   }
333
334   if (cmd->argv[1][0] == '*') {
335     if (!conn->current_channel) {
336       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
337       COMMAND_ERROR;
338       goto out;
339     }
340     name = conn->current_channel->channel_name;
341   } else {
342     name = cmd->argv[1];
343   }
344
345   if (!conn->current_channel) {
346     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
347     COMMAND_ERROR;
348     goto out;
349   }
350
351   /* Get the Channel ID of the channel */
352   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
353     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
354     COMMAND_ERROR;
355     goto out;
356   }
357
358   channel = (SilcChannelEntry)id_cache->context;
359
360   /* Send TOPIC command to the server */
361   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
362   if (cmd->argc > 2)
363     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
364                                             1, id_string, SILC_ID_CHANNEL_LEN,
365                                             2, cmd->argv[2], 
366                                             strlen(cmd->argv[2]));
367   else
368     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
369                                             1, id_string, SILC_ID_CHANNEL_LEN,
370                                             0);
371   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
372                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
373   silc_buffer_free(buffer);
374
375   /* Notify application */
376   COMMAND;
377
378  out:
379   silc_client_command_free(cmd);
380 }
381
382 /* Command INVITE. Invites specific client to join a channel. */
383
384 SILC_CLIENT_CMD_FUNC(invite)
385 {
386   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
387   SilcClient client = cmd->client;
388   SilcClientConnection conn = cmd->conn;
389   SilcClientEntry client_entry;
390   SilcChannelEntry channel_entry;
391   SilcBuffer buffer;
392   unsigned int num = 0;
393   char *nickname = NULL, *server = NULL;
394   unsigned char *client_id, *channel_id;
395
396   if (!cmd->conn) {
397     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
398     COMMAND_ERROR;
399     goto out;
400   }
401
402   if (cmd->argc != 3) {
403     cmd->client->ops->say(cmd->client, conn,
404                           "Usage: /INVITE <nickname>[@<server>] <channel>");
405     COMMAND_ERROR;
406     goto out;
407   }
408
409   /* Parse the typed nickname. */
410   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
411     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
412     COMMAND_ERROR;
413     goto out;
414   }
415
416   /* Find client entry */
417   client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
418   if (!client_entry) {
419     /* Client entry not found, it was requested thus mark this to be
420        pending command. */
421     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
422                                 silc_client_command_invite, context);
423     return;
424   }
425   
426   client_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
427
428   /* Find channel entry */
429   channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
430   if (!channel_entry) {
431     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
432     silc_free(client_id);
433     COMMAND_ERROR;
434     goto out;
435   }
436
437   channel_id = silc_id_id2str(channel_entry->id, SILC_ID_CHANNEL);
438
439   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
440                                           1, client_id, SILC_ID_CLIENT_LEN,
441                                           2, channel_id, SILC_ID_CHANNEL_LEN);
442   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
443                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
444   silc_buffer_free(buffer);
445
446   cmd->client->ops->say(cmd->client, conn, 
447                         "Inviting %s to channel %s", cmd->argv[1], 
448                         cmd->argv[2]);
449
450   /* Notify application */
451   COMMAND;
452
453  out:
454   silc_client_command_free(cmd);
455 }
456
457 /* Command QUIT. Closes connection with current server. */
458  
459 SILC_CLIENT_CMD_FUNC(quit)
460 {
461   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
462   SilcBuffer buffer;
463
464   if (!cmd->conn) {
465     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
466     COMMAND_ERROR;
467     goto out;
468   }
469
470   buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
471                                        ++cmd->argv, ++cmd->argv_lens,
472                                        ++cmd->argv_types, 0);
473   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
474                           NULL, 0, NULL, NULL, 
475                           buffer->data, buffer->len, TRUE);
476   silc_buffer_free(buffer);
477   cmd->argv--;
478   cmd->argv_lens--;
479   cmd->argv_types--;
480
481   /* Close connection */
482   silc_client_close_connection(cmd->client, cmd->conn->sock);
483   cmd->client->ops->disconnect(cmd->client, cmd->conn);
484
485   /* Notify application */
486   COMMAND;
487
488  out:
489   silc_client_command_free(cmd);
490 }
491
492 SILC_CLIENT_CMD_FUNC(kill)
493 {
494 }
495
496 /* Command INFO. Request information about specific server. If specific
497    server is not provided the current server is used. */
498
499 SILC_CLIENT_CMD_FUNC(info)
500 {
501   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
502   SilcClientConnection conn = cmd->conn;
503   SilcBuffer buffer;
504   char *name;
505
506   if (!cmd->conn) {
507     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
508     COMMAND_ERROR;
509     goto out;
510   }
511
512   if (cmd->argc < 2)
513     name = strdup(conn->remote_host);
514   else
515     name = strdup(cmd->argv[1]);
516
517   /* Send the command */
518   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
519                                           1, name, strlen(name));
520   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
521                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
522   silc_buffer_free(buffer);
523
524   /* Notify application */
525   COMMAND;
526
527  out:
528   silc_client_command_free(cmd);
529 }
530
531 SILC_CLIENT_CMD_FUNC(connect)
532 {
533 }
534
535 /* Command PING. Sends ping to server. This is used to test the 
536    communication channel. */
537
538 SILC_CLIENT_CMD_FUNC(ping)
539 {
540   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
541   SilcClientConnection conn = cmd->conn;
542   SilcBuffer buffer;
543   void *id;
544   int i;
545   char *name = NULL;
546
547   if (!cmd->conn) {
548     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
549     COMMAND_ERROR;
550     goto out;
551   }
552
553   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
554     name = strdup(conn->remote_host);
555
556   id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
557
558   /* Send the command */
559   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
560                                           1, conn->remote_id_data, 
561                                           SILC_ID_SERVER_LEN);
562   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
563                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
564   silc_buffer_free(buffer);
565
566   /* Start counting time */
567   for (i = 0; i < conn->ping_count; i++) {
568     if (conn->ping[i].dest_id == NULL) {
569       conn->ping[i].start_time = time(NULL);
570       conn->ping[i].dest_id = id;
571       conn->ping[i].dest_name = name;
572       conn->ping_count++;
573       break;
574     }
575   }
576   if (i >= conn->ping_count) {
577     i = conn->ping_count;
578     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
579     conn->ping[i].start_time = time(NULL);
580     conn->ping[i].dest_id = id;
581     conn->ping[i].dest_name = name;
582     conn->ping_count++;
583   }
584   
585   /* Notify application */
586   COMMAND;
587
588  out:
589   silc_client_command_free(cmd);
590 }
591
592 SILC_CLIENT_CMD_FUNC(oper)
593 {
594 }
595
596 SILC_CLIENT_CMD_FUNC(trace)
597 {
598 }
599
600 SILC_CLIENT_CMD_FUNC(notice)
601 {
602 }
603
604 /* Command JOIN. Joins to a channel. */
605
606 SILC_CLIENT_CMD_FUNC(join)
607 {
608   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
609   SilcClientConnection conn = cmd->conn;
610   SilcIDCacheEntry id_cache = NULL;
611   SilcBuffer buffer;
612
613   if (!cmd->conn) {
614     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
615     COMMAND_ERROR;
616     goto out;
617   }
618
619   if (cmd->argc < 2) {
620     /* Show channels currently joined to */
621
622     goto out;
623   }
624
625   /* See if we have joined to the requested channel already */
626   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
627                                     &id_cache)) {
628     cmd->client->ops->say(cmd->client, conn, 
629                           "You are talking to channel %s", cmd->argv[1]);
630     conn->current_channel = (SilcChannelEntry)id_cache->context;
631 #if 0
632     cmd->client->screen->bottom_line->channel = cmd->argv[1];
633     silc_screen_print_bottom_line(cmd->client->screen, 0);
634 #endif
635     goto out;
636   }
637
638   /* Send JOIN command to the server */
639   buffer = silc_command_payload_encode(SILC_COMMAND_JOIN,
640                                        cmd->argc - 1, ++cmd->argv,
641                                        ++cmd->argv_lens, ++cmd->argv_types, 0);
642   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
643                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
644   silc_buffer_free(buffer);
645   cmd->argv--;
646   cmd->argv_lens--;
647   cmd->argv_types--;
648
649   /* Notify application */
650   COMMAND;
651
652  out:
653   silc_client_command_free(cmd);
654 }
655
656 SILC_CLIENT_CMD_FUNC(motd)
657 {
658   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
659   SilcClientConnection conn = cmd->conn;
660   SilcBuffer buffer;
661
662   if (!cmd->conn) {
663     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
664     COMMAND_ERROR;
665     goto out;
666   }
667
668   if (cmd->argc < 1 || cmd->argc > 1) {
669     cmd->client->ops->say(cmd->client, conn,
670                           "Usage: /MOTD");
671     COMMAND_ERROR;
672     goto out;
673   }
674
675   /* Send TOPIC command to the server */
676   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
677                                           2, conn->remote_host, 
678                                           strlen(conn->remote_host));
679   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
680                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
681   silc_buffer_free(buffer);
682
683   /* Notify application */
684   COMMAND;
685
686  out:
687   silc_client_command_free(cmd);
688 }
689
690 SILC_CLIENT_CMD_FUNC(umode)
691 {
692 }
693
694 SILC_CLIENT_CMD_FUNC(cmode)
695 {
696 }
697
698 SILC_CLIENT_CMD_FUNC(kick)
699 {
700 }
701
702 SILC_CLIENT_CMD_FUNC(restart)
703 {
704 }
705  
706 SILC_CLIENT_CMD_FUNC(close)
707 {
708 }
709  
710 SILC_CLIENT_CMD_FUNC(die)
711 {
712 }
713  
714 SILC_CLIENT_CMD_FUNC(silcoper)
715 {
716 }
717
718 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
719
720 SILC_CLIENT_CMD_FUNC(leave)
721 {
722   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
723   SilcClientConnection conn = cmd->conn;
724   SilcIDCacheEntry id_cache = NULL;
725   SilcChannelEntry channel;
726   SilcBuffer buffer;
727   unsigned char *id_string;
728   char *name;
729
730   if (!cmd->conn) {
731     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
732     COMMAND_ERROR;
733     goto out;
734   }
735
736   if (cmd->argc != 2) {
737     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
738     COMMAND_ERROR;
739     goto out;
740   }
741
742   if (cmd->argv[1][0] == '*') {
743     if (!conn->current_channel) {
744       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
745       COMMAND_ERROR;
746       goto out;
747     }
748     name = conn->current_channel->channel_name;
749   } else {
750     name = cmd->argv[1];
751   }
752
753   if (!conn->current_channel) {
754     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
755     COMMAND_ERROR;
756     goto out;
757   }
758
759   /* Get the Channel ID of the channel */
760   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
761     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
762     COMMAND_ERROR;
763     goto out;
764   }
765
766   channel = (SilcChannelEntry)id_cache->context;
767
768   /* Send LEAVE command to the server */
769   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
770   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
771                                           1, id_string, SILC_ID_CHANNEL_LEN);
772   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
773                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
774   silc_buffer_free(buffer);
775
776   /* We won't talk anymore on this channel */
777   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
778
779   conn->current_channel = NULL;
780
781   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
782   silc_free(channel->channel_name);
783   silc_free(channel->id);
784   silc_free(channel->key);
785   silc_cipher_free(channel->channel_key);
786   silc_free(channel);
787   silc_free(id_string);
788
789   /* Notify application */
790   COMMAND;
791
792  out:
793   silc_client_command_free(cmd);
794 }
795
796 /* Command NAMES. Requests the names of the clients joined on requested
797    channel. */
798
799 SILC_CLIENT_CMD_FUNC(names)
800 {
801   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
802   SilcClientConnection conn = cmd->conn;
803   SilcIDCacheEntry id_cache = NULL;
804   SilcBuffer buffer;
805   char *name;
806   unsigned char *id_string;
807
808   if (!cmd->conn) {
809     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
810     COMMAND_ERROR;
811     goto out;
812   }
813
814   if (cmd->argc != 2) {
815     cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
816     COMMAND_ERROR;
817     goto out;
818   }
819
820   if (cmd->argv[1][0] == '*')
821     name = conn->current_channel->channel_name;
822   else
823     name = cmd->argv[1];
824
825   /* Get the Channel ID of the channel */
826   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
827     /* XXX should resolve the channel ID; LIST command */
828     cmd->client->ops->say(cmd->client, conn, 
829                           "You are not on that channel", name);
830     COMMAND_ERROR;
831     goto out;
832   }
833
834   /* Send NAMES command to the server */
835   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
836   buffer = silc_command_payload_encode_va(SILC_COMMAND_NAMES, 0, 1, 
837                                           1, id_string, SILC_ID_CHANNEL_LEN);
838   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
839                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
840   silc_buffer_free(buffer);
841   silc_free(id_string);
842
843   /* Register dummy pending command that will tell the reply command
844      that user called this command. Server may send reply to this command
845      even if user did not send this command thus we want to handle things
846      differently when user sent the command. This is dummy and won't be
847      execute. */
848   /* XXX this is kludge and should be removed after pending command reply 
849      support is added. Currently only commands may be pending not command
850      replies. */
851   silc_client_command_pending(SILC_COMMAND_NAMES, 
852                               silc_client_command_names, NULL);
853
854   /* Notify application */
855   COMMAND;
856
857  out:
858   silc_client_command_free(cmd);
859 }