5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 Pekka Riikonen
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.
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.
23 * Revision 1.3 2000/07/03 05:49:49 priikone
24 * Implemented LEAVE command. Minor bug fixes.
26 * Revision 1.2 2000/06/27 19:38:40 priikone
27 * Added missing goto flag.
29 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
30 * Importet from internal CVS/Added Log headers.
35 #include "clientincludes.h"
37 /* Client command list. */
38 SilcClientCommand silc_command_list[] =
40 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
41 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
42 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
43 SILC_CF_LAG | SILC_CF_REG, 3),
44 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
45 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
46 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 2),
48 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
49 SILC_CLIENT_CMD(kill, KILL, "KILL",
50 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
51 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
52 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
53 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
55 SILC_CLIENT_CMD(oper, OPER, "OPER",
56 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
57 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
58 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
59 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
60 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
61 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
62 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
63 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
64 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
65 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
66 SILC_CLIENT_CMD(die, DIE, "DIE",
67 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
68 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
69 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
70 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
71 SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
74 * Local. client specific commands
76 SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
77 SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
78 SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
79 SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
80 SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
81 SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
86 #define SILC_NOT_CONNECTED(x) \
87 silc_say((x), "You are not connected to a server, use /SERVER to connect");
89 /* List of pending commands. */
90 SilcClientCommandPending *silc_command_pending = NULL;
92 /* Add new pending command to the list of pending commands. Currently
93 pending commands are executed from command replies, thus we can
94 execute any command after receiving some specific command reply.
96 The argument `reply_cmd' is the command reply from where the callback
97 function is to be called, thus, it IS NOT the command to be executed.
99 XXX: If needed in the future this support may be extended for
100 commands as well, when any command could be executed after executing
101 some specific command. */
103 void silc_client_command_pending(SilcCommand reply_cmd,
104 SilcClientCommandCallback callback,
107 SilcClientCommandPending *reply, *r;
109 reply = silc_calloc(1, sizeof(*reply));
110 reply->reply_cmd = reply_cmd;
111 reply->context = context;
112 reply->callback = callback;
114 if (silc_command_pending == NULL) {
115 silc_command_pending = reply;
119 for (r = silc_command_pending; r; r = r->next) {
120 if (r->next == NULL) {
127 /* Deletes pending command by reply command type. */
129 void silc_client_command_pending_del(SilcCommand reply_cmd)
131 SilcClientCommandPending *r, *tmp;
133 if (silc_command_pending) {
134 if (silc_command_pending->reply_cmd == reply_cmd) {
135 silc_free(silc_command_pending);
136 silc_command_pending = NULL;
140 for (r = silc_command_pending; r; r = r->next) {
141 if (r->next && r->next->reply_cmd == reply_cmd) {
143 r->next = r->next->next;
151 /* Free command context and its internals */
153 static void silc_client_command_free(SilcClientCommandContext cmd)
158 for (i = 0; i < cmd->argc; i++)
159 silc_free(cmd->argv[i]);
164 /* Command WHOIS. This command is used to query information about
167 SILC_CLIENT_CMD_FUNC(whois)
169 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
172 if (cmd->argc < 2 || cmd->argc > 3) {
173 silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
177 if (!cmd->client->current_win->sock) {
178 SILC_NOT_CONNECTED(cmd->client);
182 buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
183 cmd->argc - 1, ++cmd->argv,
184 ++cmd->argv_lens, ++cmd->argv_types);
185 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
186 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
187 buffer->data, buffer->len, TRUE);
188 silc_buffer_free(buffer);
194 silc_client_command_free(cmd);
197 SILC_CLIENT_CMD_FUNC(whowas)
201 /* Command IDENTIFY. This command is used to query information about
202 specific user, especially ID's. */
204 SILC_CLIENT_CMD_FUNC(identify)
206 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
209 if (cmd->argc < 2 || cmd->argc > 3) {
210 silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
214 if (!cmd->client->current_win->sock) {
215 SILC_NOT_CONNECTED(cmd->client);
219 buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
220 cmd->argc - 1, ++cmd->argv,
221 ++cmd->argv_lens, ++cmd->argv_types);
222 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
223 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
224 buffer->data, buffer->len, TRUE);
225 silc_buffer_free(buffer);
231 silc_client_command_free(cmd);
234 /* Command NICK. Shows current nickname/sets new nickname on current
237 SILC_CLIENT_CMD_FUNC(nick)
239 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
240 SilcClientWindow win = NULL;
244 SILC_NOT_CONNECTED(cmd->client);
248 /* Show current nickname */
251 silc_say(cmd->client, "Your nickname is %s on server %s",
252 win->nickname, win->remote_host);
254 silc_say(cmd->client, "Your nickname is %s", win->nickname);
259 win = (SilcClientWindow)cmd->sock->user_data;
261 /* Set new nickname */
262 buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
263 cmd->argc - 1, ++cmd->argv,
264 ++cmd->argv_lens, ++cmd->argv_types);
265 silc_client_packet_send(cmd->client, cmd->sock,
266 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
267 buffer->data, buffer->len, TRUE);
268 silc_buffer_free(buffer);
273 silc_free(win->nickname);
274 win->nickname = strdup(cmd->argv[1]);
277 silc_client_command_free(cmd);
280 /* Command SERVER. Connects to remote SILC server. This is local command. */
282 SILC_CLIENT_CMD_FUNC(server)
284 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
289 /* Show current servers */
290 if (!cmd->client->current_win->sock) {
291 silc_say(cmd->client, "You are not connected to any server");
292 silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
299 /* See if port is included and then extract it */
300 if (strchr(cmd->argv[1], ':')) {
301 len = strcspn(cmd->argv[1], ":");
302 hostname = silc_calloc(len + 1, sizeof(char));
303 memcpy(hostname, cmd->argv[1], len);
304 port = atoi(cmd->argv[1] + 1 + len);
306 hostname = cmd->argv[1];
310 /* Connect asynchronously to not to block user interface */
311 silc_client_connect_to_server(cmd->client, port, hostname);
314 silc_client_command_free(cmd);
317 SILC_CLIENT_CMD_FUNC(list)
321 SILC_CLIENT_CMD_FUNC(topic)
325 SILC_CLIENT_CMD_FUNC(invite)
329 /* Command QUIT. Closes connection with current server. */
331 SILC_CLIENT_CMD_FUNC(quit)
333 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
336 if (!cmd->client->current_win->sock) {
337 SILC_NOT_CONNECTED(cmd->client);
341 buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1,
342 ++cmd->argv, ++cmd->argv_lens,
344 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
345 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
346 buffer->data, buffer->len, TRUE);
347 silc_buffer_free(buffer);
352 /* Close connection */
353 silc_client_close_connection(cmd->client, cmd->sock);
354 cmd->client->screen->bottom_line->connection = NULL;
355 silc_screen_print_bottom_line(cmd->client->screen, 0);
358 silc_client_command_free(cmd);
361 SILC_CLIENT_CMD_FUNC(kill)
365 SILC_CLIENT_CMD_FUNC(info)
369 SILC_CLIENT_CMD_FUNC(connect)
373 SILC_CLIENT_CMD_FUNC(ping)
377 SILC_CLIENT_CMD_FUNC(oper)
381 SILC_CLIENT_CMD_FUNC(trace)
385 SILC_CLIENT_CMD_FUNC(notice)
389 /* Command JOIN. Joins to a channel. */
391 SILC_CLIENT_CMD_FUNC(join)
393 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
394 SilcClientWindow win = NULL;
395 SilcIDCache *id_cache = NULL;
398 #define CIDC(x) win->channel_id_cache[(x) - 32]
399 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
402 /* Show channels currently joined to */
403 if (!cmd->client->current_win->sock) {
404 silc_say(cmd->client, "No current channel for this window");
405 SILC_NOT_CONNECTED(cmd->client);
413 if (!cmd->client->current_win->sock) {
414 SILC_NOT_CONNECTED(cmd->client);
418 win = (SilcClientWindow)cmd->sock->user_data;
420 /* See if we have joined to the requested channel already */
421 silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]),
422 cmd->argv[1], &id_cache);
425 silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
426 win->current_channel = (SilcChannelEntry)id_cache->context;
427 cmd->client->screen->bottom_line->channel = cmd->argv[1];
428 silc_screen_print_bottom_line(cmd->client->screen, 0);
432 /* Send JOIN command to the server */
433 buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
434 cmd->argc - 1, ++cmd->argv,
435 ++cmd->argv_lens, ++cmd->argv_types);
436 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
437 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
438 buffer->data, buffer->len, TRUE);
439 silc_buffer_free(buffer);
445 silc_client_command_free(cmd);
450 SILC_CLIENT_CMD_FUNC(motd)
454 SILC_CLIENT_CMD_FUNC(umode)
458 SILC_CLIENT_CMD_FUNC(cmode)
462 SILC_CLIENT_CMD_FUNC(kick)
466 SILC_CLIENT_CMD_FUNC(restart)
470 SILC_CLIENT_CMD_FUNC(close)
474 SILC_CLIENT_CMD_FUNC(die)
478 SILC_CLIENT_CMD_FUNC(silcoper)
482 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
484 SILC_CLIENT_CMD_FUNC(leave)
486 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
487 SilcClientWindow win = NULL;
488 SilcIDCache *id_cache = NULL;
490 unsigned char *id_string;
493 #define CIDC(x) win->channel_id_cache[(x) - 32]
494 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
496 if (cmd->argc != 2) {
497 silc_say(cmd->client, "Usage: /LEAVE <channel>");
501 if (!cmd->client->current_win->sock) {
502 SILC_NOT_CONNECTED(cmd->client);
506 win = (SilcClientWindow)cmd->sock->user_data;
508 if (!win->current_channel) {
509 silc_say(cmd->client, "You are not on that channel", name);
513 if (cmd->argv[1][0] == '*')
514 name = win->current_channel->channel_name;
518 /* Get the Channel ID of the channel */
519 silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
521 silc_say(cmd->client, "You are not on that channel", name);
525 /* Send LEAVE command to the server */
526 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
527 buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1,
528 id_string, SILC_ID_CHANNEL_LEN);
529 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
530 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
531 buffer->data, buffer->len, TRUE);
532 silc_buffer_free(buffer);
534 /* We won't talk anymore on this channel */
535 silc_say(cmd->client, "You have left channel %s", name);
536 cmd->client->screen->bottom_line->channel = NULL;
537 silc_screen_print_bottom_line(cmd->client->screen, 0);
539 silc_idcache_del_by_id(CIDC(name[0]), CIDCC(name[0]),
540 SILC_ID_CHANNEL, win->current_channel->id);
541 silc_free(win->current_channel->channel_name);
542 silc_free(win->current_channel->id);
543 silc_free(win->current_channel->key);
544 silc_cipher_free(win->current_channel->channel_key);
545 silc_free(win->current_channel);
546 win->current_channel = NULL;
549 silc_client_command_free(cmd);
554 SILC_CLIENT_CMD_FUNC(names)
562 /* HELP command. This is local command and shows help on SILC */
564 SILC_CLIENT_CMD_FUNC(help)
569 /* CLEAR command. This is local command and clears current output window */
571 SILC_CLIENT_CMD_FUNC(clear)
573 SilcClient client = (SilcClient)context;
575 assert(client->current_win != NULL);
576 wclear((WINDOW *)client->current_win->screen);
577 wrefresh((WINDOW *)client->current_win->screen);
580 /* VERSION command. This is local command and shows version of the client */
582 SILC_CLIENT_CMD_FUNC(version)
587 /* Command MSG. Sends private message to user or list of users. */
588 /* XXX supports only one destination */
590 SILC_CLIENT_CMD_FUNC(msg)
592 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
593 SilcClientWindow win = NULL;
594 SilcClient client = cmd->client;
596 SilcIDCache *id_cache;
597 unsigned int nick_len;
600 silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
604 if (!cmd->client->current_win->sock) {
605 SILC_NOT_CONNECTED(cmd->client);
609 win = (SilcClientWindow)cmd->sock->user_data;
611 #define CIDC(x) win->client_id_cache[(x) - 32], \
612 win->client_id_cache_count[(x) - 32]
614 /* Find ID from cache */
615 if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1],
616 &id_cache) == FALSE) {
617 SilcClientCommandContext ctx;
620 SILC_LOG_DEBUG(("Requesting Client ID from server"));
622 /* No ID found. Do query from the server. The query is done by
623 sending simple IDENTIFY command to the server. */
624 ctx = silc_calloc(1, sizeof(*ctx));
625 ctx->client = client;
626 ctx->sock = cmd->sock;
627 memset(ident, 0, sizeof(ident));
628 snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
629 silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
630 &ctx->argv_types, &ctx->argc, 2);
631 silc_client_command_identify(ctx);
633 /* Mark this command to be pending command and to be executed after
634 we have received the IDENTIFY reply from server. */
635 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
636 silc_client_command_msg, context);
640 /* Display the message for our eyes. */
641 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
643 /* Send the private message */
644 silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
645 cmd->argv[2], cmd->argv_lens[2],
649 silc_client_command_free(cmd);
653 SILC_CLIENT_CMD_FUNC(away)