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.
22 #include "clientlibincludes.h"
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
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),
60 { NULL, 0, NULL, 0, 0 },
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");
67 /* Command operation that is called at the end of all commands.
69 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
70 cmd, TRUE, cmd->command->cmd)
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)
76 /* List of pending commands. */
77 SilcClientCommandPending *silc_command_pending = NULL;
79 /* Finds and returns a pointer to the command list. Return NULL if the
80 command is not found. */
82 SilcClientCommand *silc_client_command_find(const char *name)
84 SilcClientCommand *cmd;
86 for (cmd = silc_command_list; cmd->name; cmd++) {
87 if (!strcmp(cmd->name, name))
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.
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.
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. */
105 void silc_client_command_pending(SilcCommand reply_cmd,
106 SilcClientCommandCallback callback,
109 SilcClientCommandPending *reply, *r;
111 reply = silc_calloc(1, sizeof(*reply));
112 reply->reply_cmd = reply_cmd;
113 reply->context = context;
114 reply->callback = callback;
116 if (silc_command_pending == NULL) {
117 silc_command_pending = reply;
121 for (r = silc_command_pending; r; r = r->next) {
122 if (r->next == NULL) {
129 /* Deletes pending command by reply command type. */
131 void silc_client_command_pending_del(SilcCommand reply_cmd)
133 SilcClientCommandPending *r, *tmp;
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;
142 for (r = silc_command_pending; r; r = r->next) {
143 if (r->next && r->next->reply_cmd == reply_cmd) {
145 r->next = r->next->next;
153 /* Free command context and its internals */
155 void silc_client_command_free(SilcClientCommandContext cmd)
160 for (i = 0; i < cmd->argc; i++)
161 silc_free(cmd->argv[i]);
166 /* Command WHOIS. This command is used to query information about
169 SILC_CLIENT_CMD_FUNC(whois)
171 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
172 SilcClientConnection conn = cmd->conn;
176 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
181 if (cmd->argc < 2 || cmd->argc > 3) {
182 cmd->client->ops->say(cmd->client, conn,
183 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
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);
199 /* Notify application */
203 silc_client_command_free(cmd);
206 SILC_CLIENT_CMD_FUNC(whowas)
210 /* Command IDENTIFY. This command is used to query information about
211 specific user, especially ID's. */
213 SILC_CLIENT_CMD_FUNC(identify)
215 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
216 SilcClientConnection conn = cmd->conn;
220 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
225 if (cmd->argc < 2 || cmd->argc > 3) {
226 cmd->client->ops->say(cmd->client, conn,
227 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
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);
243 /* Notify application */
247 silc_client_command_free(cmd);
250 /* Command NICK. Shows current nickname/sets new nickname on current
253 SILC_CLIENT_CMD_FUNC(nick)
255 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
256 SilcClientConnection conn = cmd->conn;
260 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
265 /* Show current nickname */
268 cmd->client->ops->say(cmd->client, conn,
269 "Your nickname is %s on server %s",
270 conn->nickname, conn->remote_host);
272 cmd->client->ops->say(cmd->client, conn,
273 "Your nickname is %s", conn->nickname);
275 /* XXX Notify application */
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);
292 silc_free(conn->nickname);
293 conn->nickname = strdup(cmd->argv[1]);
295 /* Notify application */
299 silc_client_command_free(cmd);
302 SILC_CLIENT_CMD_FUNC(list)
306 SILC_CLIENT_CMD_FUNC(topic)
310 /* Command INVITE. Invites specific client to join a channel. */
312 SILC_CLIENT_CMD_FUNC(invite)
314 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
315 SilcClient client = cmd->client;
316 SilcClientConnection conn = cmd->conn;
317 SilcClientEntry client_entry;
318 SilcChannelEntry channel_entry;
320 unsigned int num = 0;
321 char *nickname = NULL, *server = NULL;
322 unsigned char *client_id, *channel_id;
325 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
330 if (cmd->argc != 3) {
331 cmd->client->ops->say(cmd->client, conn,
332 "Usage: /INVITE <nickname>[@<server>] <channel>");
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");
344 /* Find client entry */
345 client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
347 /* Client entry not found, it was requested thus mark this to be
349 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
350 silc_client_command_invite, context);
354 client_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
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);
365 channel_id = silc_id_id2str(channel_entry->id, SILC_ID_CHANNEL);
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);
374 cmd->client->ops->say(cmd->client, conn,
375 "Inviting %s to channel %s", cmd->argv[1],
378 /* Notify application */
382 silc_client_command_free(cmd);
385 /* Command QUIT. Closes connection with current server. */
387 SILC_CLIENT_CMD_FUNC(quit)
389 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
393 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
398 buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1,
399 ++cmd->argv, ++cmd->argv_lens,
401 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
403 buffer->data, buffer->len, TRUE);
404 silc_buffer_free(buffer);
409 /* Close connection */
410 silc_client_close_connection(cmd->client, cmd->conn->sock);
411 cmd->client->ops->disconnect(cmd->client, cmd->conn);
413 /* Notify application */
417 silc_client_command_free(cmd);
420 SILC_CLIENT_CMD_FUNC(kill)
424 /* Command INFO. Request information about specific server. If specific
425 server is not provided the current server is used. */
427 SILC_CLIENT_CMD_FUNC(info)
429 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
430 SilcClientConnection conn = cmd->conn;
435 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
441 name = strdup(conn->remote_host);
443 name = strdup(cmd->argv[1]);
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);
452 /* Notify application */
456 silc_client_command_free(cmd);
459 SILC_CLIENT_CMD_FUNC(connect)
463 /* Command PING. Sends ping to server. This is used to test the
464 communication channel. */
466 SILC_CLIENT_CMD_FUNC(ping)
468 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
469 SilcClientConnection conn = cmd->conn;
476 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
481 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
482 name = strdup(conn->remote_host);
484 id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
486 /* Send the command */
487 buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1,
488 1, conn->remote_id_data,
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);
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;
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;
513 /* Notify application */
517 silc_client_command_free(cmd);
520 SILC_CLIENT_CMD_FUNC(oper)
524 SILC_CLIENT_CMD_FUNC(trace)
528 SILC_CLIENT_CMD_FUNC(notice)
532 /* Command JOIN. Joins to a channel. */
534 SILC_CLIENT_CMD_FUNC(join)
536 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
537 SilcClientConnection conn = cmd->conn;
538 SilcIDCacheEntry id_cache = NULL;
542 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
548 /* Show channels currently joined to */
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],
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;
560 cmd->client->screen->bottom_line->channel = cmd->argv[1];
561 silc_screen_print_bottom_line(cmd->client->screen, 0);
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);
577 /* Notify application */
581 silc_client_command_free(cmd);
584 SILC_CLIENT_CMD_FUNC(motd)
588 SILC_CLIENT_CMD_FUNC(umode)
592 SILC_CLIENT_CMD_FUNC(cmode)
596 SILC_CLIENT_CMD_FUNC(kick)
600 SILC_CLIENT_CMD_FUNC(restart)
604 SILC_CLIENT_CMD_FUNC(close)
608 SILC_CLIENT_CMD_FUNC(die)
612 SILC_CLIENT_CMD_FUNC(silcoper)
616 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
618 SILC_CLIENT_CMD_FUNC(leave)
620 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
621 SilcClientConnection conn = cmd->conn;
622 SilcIDCacheEntry id_cache = NULL;
623 SilcChannelEntry channel;
625 unsigned char *id_string;
629 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
634 if (cmd->argc != 2) {
635 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
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");
646 name = conn->current_channel->channel_name;
651 if (!conn->current_channel) {
652 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
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");
664 channel = (SilcChannelEntry)id_cache->context;
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);
674 /* We won't talk anymore on this channel */
675 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
677 conn->current_channel = NULL;
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);
685 silc_free(id_string);
687 /* Notify application */
691 silc_client_command_free(cmd);
694 /* Command NAMES. Requests the names of the clients joined on requested
697 SILC_CLIENT_CMD_FUNC(names)
699 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
700 SilcClientConnection conn = cmd->conn;
701 SilcIDCacheEntry id_cache = NULL;
704 unsigned char *id_string;
707 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
712 if (cmd->argc != 2) {
713 cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
718 if (cmd->argv[1][0] == '*')
719 name = conn->current_channel->channel_name;
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);
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);
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
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
749 silc_client_command_pending(SILC_COMMAND_NAMES,
750 silc_client_command_names, NULL);
752 /* Notify application */
756 silc_client_command_free(cmd);