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