/*
Silky - A GTK+ client for SILC.
Copyright (C) 2003, 2004, 2005 Toni Willberg
- command functions, utilities
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
http://silky.sourceforge.net/
*/
#include <glade/glade.h>
#include <gtk/gtk.h>
#include <silcincludes.h>
#include <silcclient.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ui_console.h"
#include "ui_query.h"
#include "support.h"
#include "commands.h"
#include "preferences.h"
#include "log.h"
#include "callbacks.h"
#include "buddylist.h"
#include "gui.h"
#include "xmllayout.h"
#include "ui_channel.h" /* get_active_channel_name() FIXME, shouldn't be used */
#include "common.h"
extern silkyStruct *silky;
extern SilkyXMLElement xml_elements[];
extern GladeXML *xmlmain;
extern gchar *previous_nick; /* my nickname just before sending NICK command */
extern SilkyChannel *channels;
/*
cmd_msg callback thingy
This is called after user does /MSG nickname
*/
void on_new_msg_resolved_cmd_msg(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void *context) {
gint value;
gchar *message;
SilcClientID *clientid;
gchar *str_clientid;
SilcMessageFlags sendflags;
SilcClientEntry cliententry;
debug("found '%d' clients", clients_count);
if (clients_count == 0) {
debug("\tDid not find anyone");
errordialog(_("No users found."));
return;
}
if (clients_count > 1) {
printconsole(_("There are many people with same nickname, message not sent. Please specify the target."));
/* FIXME: make sure this isn't illegal way */
on_show_new_query_window(NULL, NULL);
return;
/*
for (index=0; index < clients_count; index++) {
debug("\tFound %s@%s (%s@%s) '%s' (%s)",
clients[index]->nickname,
clients[index]->server,
clients[index]->username,
clients[index]->hostname,
clients[index]->realname,
silc_fingerprint(clients[index]->fingerprint,
clients[index]->fingerprint_len )
);
}
*/
// FIXME: ask user which user to send the message to!
}
clientid = clients[0]->id;
str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT);
cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid);
if (!cliententry) {
debug("Did not get cliententry on_send..");
printconsole(_("No such user."));
return;
}
message = strdup(context);
if (strlen(message) <= 0) {
debug("empty message, skipped");
return;
}
sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
value = prefs_str_to_bool(prefs_get("sign_private_messages"));
/* whetter to SIGN message or not */
if (value) {
sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
}
silc_client_send_private_message(silky->client, silky->conn, cliententry,
sendflags, message,
strlen(message), FALSE); /* force send TRUE */
debug_info("private message sent to %s.", cliententry->nickname);
printquery(clientid, silky->conn->nickname, sendflags, message);
}
/* --------------------------------------------------------- */
/* COMMAND HANDLING STUFF STARTS HERE */
static int
cmd_help (char *tbuf, char *word[], char *word_eol[])
{
int i = 0, longfmt = 0;
gchar *helpcmd = "", *buf = malloc(4096);
debug("cmd_help()");
if (tbuf)
helpcmd = word[2];
if (*helpcmd && strcmp (helpcmd, "-l") == 0)
longfmt = 1;
if (*helpcmd && !longfmt)
{
command_help (tbuf, helpcmd, FALSE);
} else
{
/*struct popup *pop;*/
/* GSList *list = command_list; for user commands */
int t = 1, j;
snprintf(buf, 4096, "\n%s:\n\n ", _("Commands Available"));
if (longfmt)
{
while (1)
{
if (!xc_cmds[i].name)
break;
if (!xc_cmds[i].help || *xc_cmds[i].help == '\0')
g_snprintf (buf + strlen(buf), 4096, " %s :\n", xc_cmds[i].name);
else
g_snprintf (buf + strlen(buf), 4096, " %s : %s\n", xc_cmds[i].name, _(xc_cmds[i].help));
printconsole (buf);
i++;
}
buf[0] = 0;
} else
{
while (1)
{
if (!xc_cmds[i].name)
break;
strcat (buf, xc_cmds[i].name);
t++;
if (t == 6)
{
t = 1;
strcat (buf, "\n ");
} else
for (j = 0; j < (10 - strlen (xc_cmds[i].name)); j++)
strcat (buf, " ");
i++;
}
}
g_snprintf( buf + strlen(buf), 4096, "\n\n%s\n\n",
_("Type /HELP <command> for more information, or /HELP -l") );
g_snprintf( buf + strlen(buf), 4096, "%s:\n\n ", _("User defined commands") );
t = 1;
/* while (list)
{
pop = (struct popup *) list->data;
strcat (buf, pop->name);
t++;
if (t == 6)
{
t = 1;
strcat (buf, "\n");
printconsole (buf);
strcpy (buf, " ");
} else
{
if (strlen (pop->name) < 10)
{
for (j = 0; j < (10 - strlen (pop->name)); j++)
strcat (buf, " ");
}
}
list = list->next;
}*/
strcat (buf, "\n");
printconsole (buf);
free (buf);
printconsole( g_strdup_printf("\n%s:\n", _("Plugin defined commands")) );
}
return TRUE;
}
/* There must be a better way to do this? perhaps change on_ui_send to support actions */
static int
cmd_me (char *tbuf, char *word[], char *word_eol[])
{
gchar *channel_name;
SilcChannelEntry channel;
GtkEntry *inmes;
gchar *message;
SilcMessageFlags sendflags;
GtkNotebook *storednotebook;
GtkWidget *active_page;
SilcClientID *clientid;
gchar *str_clientid;
SilcClientEntry cliententry;
gint value; /* from config */
debug_info("cmd_me()");
/* Get value of the input box */
inmes = GTK_ENTRY(glade_xml_get_widget (xmlmain, "inputbox"));
/* Show an error dialog if there's no connection */
if (!CONNECTED) {
debug("Can not send a message.\nYou are not connected to a server.");
printconsole(_("You are not connected to server."));
return 0;
}
/* get active channel name from UI (support.c) */
channel_name = get_active_channel_name();
debug("\treturned from get_active_channel_name");
/* was it channel message or query message */
if (channel_name) {
debug("sending channel message");
/* get channel by name */
channel = silc_client_get_channel(silky->client, silky->conn, channel_name);
if (!channel) {
debug("can not find channel entry!");
printconsole(_("Can not send message at this time."));
return 0;
}
else {
debug("got channel");
}
/* get a copy of the message, next function wants other type */
message = strdup(word_eol[2]);
if (!message || strlen(message) <= 0) {
debug("empty message, skipping!");
return FALSE;
}
sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
sendflags |= SILC_MESSAGE_FLAG_ACTION;
value = prefs_str_to_bool(prefs_get("sign_channel_actions"));
/* whetter to SIGN message or not */
if (value) {
sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
}
silc_client_send_channel_message(silky->client, silky->conn, channel,
NULL, sendflags,
message, strlen(message), FALSE);
print_mime_to_channel( silky->conn->local_entry, channel->channel_name, 0, g_strdup("text/plain"), (int)sendflags, message);
/* Clear the textbox */
gtk_entry_set_text(GTK_ENTRY(inmes), "");
free(message);
}
else {
debug("Was not channel message.");
}
storednotebook = GTK_NOTEBOOK(glade_xml_get_widget (xmlmain, "tabs"));
active_page = gtk_notebook_get_nth_page(storednotebook, gtk_notebook_get_current_page(GTK_NOTEBOOK(storednotebook) ) );
clientid = g_object_get_data(G_OBJECT(active_page), "clientid");
if (clientid) {
debug("sending query/private message");
str_clientid = silc_id_id2str(clientid, SILC_ID_CLIENT);
cliententry = silc_client_get_client_by_id(silky->client, silky->conn, clientid);
if (!cliententry) {
debug("Did not get cliententry on_send..");
printconsole(_("No such user."));
return 0;
}
debug("target: '%s'", cliententry->nickname);
/* get a copy of the message, next function wants other type */
/* so we don't send the /me part */
message = strdup(word_eol[2]);
if (strlen(message) <= 0) {
return FALSE;
}
sendflags = SILC_MESSAGE_FLAG_UTF8; /* set UTF */
sendflags |= SILC_MESSAGE_FLAG_ACTION;
value = prefs_str_to_bool(prefs_get("sign_private_actions"));
/* wether to SIGN message or not */
if (value) {
sendflags |= SILC_MESSAGE_FLAG_SIGNED; /* set SIGNED */
}
silc_client_send_private_message(silky->client, silky->conn, cliententry,
sendflags, message,
strlen(message), FALSE); /* force send TRUE */
debug("private message sent.");
printquery(clientid, silky->conn->nickname, sendflags, message);
/* Clear the textbox */
gtk_entry_set_text(GTK_ENTRY(inmes), "");
}
else {
debug("Was not private message");
}
return 2;
}
static int
cmd_nick (char *tbuf, char *word[], char *word_eol[])
{
const gchar *new_nick;
const gchar *old_nick;
debug_info("cmd_nick()");
if (!CONNECTED) {
printconsole(_("You are not connected to server."));
return FALSE;
}
new_nick = strdup(word[2]);
old_nick = silky->conn->nickname;
if (strlen(new_nick) <1 ) {
printconsole(_("Nickname can not be empty."));
return FALSE;
}
if (new_nick && old_nick && !strcmp(new_nick, old_nick) ) {
printconsole(_("That is your current nickname."));
return FALSE;
}
/* set global variable for later use */
previous_nick = strdup(old_nick);
silc_client_command_call(silky->client, silky->conn, NULL, "NICK", new_nick, NULL);
debug("\tCommand sent. New entry says the nick is '%s'", silky->conn->nickname);
return 2;
}
/* sends a raw <COMMAND> [parameters] to the client library */
static int
cmd_raw (char *tbuf, char *word[], char *word_eol[])
{
gchar *cmd = strdup(word_eol[2]);
debug_info("cmd_raw(%s)", cmd );
if (silc_client_command_call(silky->client, silky->conn, cmd)) {
debug("cmd was success");
return 2;
}
else {
debug("cmd failed");
}
return 2;
}
static int
cmd_part (char *tbuf, char *word[], char *word_eol[])
{
gchar *channel_name;
/* SilcBuffer idp;*/
debug_info("cmd_part()");
/* get the channel name */
if (*word[2] && *word[2] != '*' ) { /* name given and it's not * */
channel_name = word[2];
}
else {
/* part current channel */
channel_name = get_active_channel_name();
}
/* require some channel name */
if (!channel_name || strlen(channel_name) < 1) {
debug("No channel name given, can not part.");
return FALSE;
}
if (!CONNECTED){
return FALSE;
}
if (channel_name) {
debug("leaving channel '%s'", channel_name);
printconsole(g_strdup_printf(_("Leaving channel '%s'."), channel_name));
silc_client_command_call(silky->client, silky->conn, NULL, "LEAVE", channel_name, NULL);
}
else {
debug("was not able to find channel name, leave aborted");
}
return 2;
}
static int
cmd_join (char *tbuf, char *word[], char *word_eol[])
{
const gchar *channel_name;
debug_info("cmd_join()");
/* get the channel name */
channel_name = word[2];
/* require some channel name */
if (!strlen(channel_name)) {
debug("No channel name given, can not join.");
return FALSE;
}
if (!CONNECTED){
debug("No connection. FIXME, should offer reconnection?");
return 0;
}
silky_join_channel(channel_name);
return 2;
}
/*
Used when quitting from Silky.
*/
static int
cmd_quit (char *tbuf, char *word[], char *word_eol[])
{
gchar quitmsg[129];
gint size = sizeof(quitmsg);
*quitmsg = '\0';
debug_info("cmd_quit");
if (strlen(word[2])) { /* no need to populate quitmsg if !firstarg */
debug("quitting with reason '%s'", word[2]);
if (strlen(word_eol[2]) > 128) {
size = sizeof(quitmsg); /* let's set correct length so we can nul-terminate it */
} else {
size = strlen(word_eol[2]);
}
strncpy(quitmsg, word_eol[2], size);
quitmsg[size] = '\0';
} else {
debug("quit without parameter, using default\n");
strncpy(quitmsg, prefs_get("quit_message"), size);
}
if (CONNECTED) {
debug("setting state to STATE_DISCONNECTING");
silky->state = STATE_DISCONNECTING;
printconsole(_("Disconnecting..."));
silc_client_command_call(silky->client, silky->conn, NULL, "QUIT", quitmsg, NULL);
/* the callback, silky_finish_disconnect, will set the state */
}
else {
debug("not connected, no need to disconnect\n");
}
printconsole(_("Quitting. Thank you for using Silky!"));
while (gtk_events_pending()) {
gtk_main_iteration_do(FALSE); /* tell GTK to print previous stuff before we enter blocking command next... */
}
/* tell GTK we are leaving */
gtk_main_quit();
return 2; /* we should never reach here */
}
/*
Clears the textview of active tab
*/
static int
cmd_clear ()
{
GtkTextView *textview = silky_get_active_textview();
GtkTextBuffer *textbuffer = gtk_text_view_get_buffer(textview);
debug_info("clearning textview");
if (textbuffer) {
gtk_text_buffer_set_text(textbuffer, "", 0); /* clear the view */
debug("done");
return 1;
} else {
debug("textbuffer not found");
return 0;
}
}
/*
Disconnects from connected server
*/
static int
cmd_disconnect (char *tbuf, char *word[], char *word_eol[])
{
gchar quitmsg[129];
gint size = sizeof(quitmsg);
if (DISCONNECTED) {
debug_info ("STATE_DISCONNECTED, aborting");
printconsole(_("You are not connected to server."));
return FALSE;
}
if (DISCONNECTING) {
debug_info("currently disconnecting, STATE_DISCONNECTING");
printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
return FALSE;
}
if ( CONNECTING ) {
debug_info("connection already in progress, STATE_CONNECTING");
printconsole(_("You are currently connecting to a server, please wait."));
return FALSE;
}
*quitmsg = '\0';
debug_info("on cmd_disconnect");
if (strlen(word[2])) { /* no need to populate quitmsg if !firstarg */
if (strlen(word_eol[2]) > 128) {
size = sizeof(quitmsg); /* let's set correct length so we can nul-terminate it */
} else {
size = strlen(word_eol[2]);
}
strncpy(quitmsg, word_eol[2], size);
quitmsg[size] = '\0';
} else {
strncpy(quitmsg, prefs_get("quit_message"), size);
}
debug("setting state to STATE_DISCONNECTING");
silky->state = STATE_DISCONNECTING;
printconsole(_("Disconnecting..."));
silc_client_command_call(silky->client, silky->conn, NULL, "QUIT", quitmsg, NULL);
debug("command sent");
return 2;
}
/*
Connects to a server
*/
static int
cmd_server (char *tbuf, char *word[], char *word_eol[])
{
gchar *new_server;
int ret;
char *port;
char *pass;
GtkWidget *menuwidget;
debug_info("cmd_server");
/* connection ok */
if ( CONNECTED ) {
debug_info("already connected, STATE_CONNECTED");
printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
return FALSE;
}
/* just connecting */
if ( CONNECTING ) {
debug_info("connection already in progress, STATE_CONNECTING");
printconsole(_("You are already connecting to a server, please wait."));
return FALSE;
}
if ( DISCONNECTING ) {
debug_info("currently disconnecting, STATE_DISCONNECTING");
printconsole(g_strdup_printf(_("You are already connected to '%s'."), silky->conn->remote_host) );
return FALSE;
}
/*window = glade_xml_get_widget (xmlmain, "window_connect_server");
input = GTK_ENTRY(glade_xml_get_widget (xmlmain, "new_servername"));
new_server = strdup(gtk_entry_get_text(input));*/
new_server = strdup(word[2]);
port = strdup(word[3]);
/* not used yet? */
pass = strdup(word[4]);
if (strlen(new_server) < 1) {
printconsole(_("Invalid server address."));
return FALSE;
}
debug("Setting state to STATE_CONNECTING");
silky->state = STATE_CONNECTING;
/* now let's disable Connect and Disconnect for a while, other will be
enabled later */
menuwidget = glade_xml_get_widget (xmlmain, "menu_server_disconnect");
gtk_widget_set_sensitive(menuwidget, FALSE);
menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect");
gtk_widget_set_sensitive(menuwidget, FALSE);
/* close all tabs, but console tab, on reconnection */
close_all_tabs();
printconsole(g_strdup_printf(_("Connecting to server '%s'..."), new_server));
if (*port) {
ret = silc_client_connect_to_server(silky->client, NULL, atoi(port),
new_server, silky);
}
else { /* default port */
ret = silc_client_connect_to_server(silky->client, NULL, 706,
new_server, silky);
}
if (ret == -1) {
debug_info("Can not connect to the server. (why?), setting STATE_DISCONNECTED");
silky->state = STATE_DISCONNECTED;
printconsole(_("Can not connect to the server.")); /* FIXME: why can't ?? */
menuwidget = glade_xml_get_widget (xmlmain, "menu_server_connect");
gtk_widget_set_sensitive(menuwidget, TRUE);
return 0;
}
else {
debug_info("\tConnected to %s.", new_server);
/* state and menu will be set in the callback function */
}
return 2;
}
static int
cmd_msg(char *tbuf, char *word[], char *word_eol[]) {
gchar *msgnick;
gchar *msg;
debug_info("cmd_msg()");
msgnick = strdup(word[2]);
if (!msgnick) {
debug("Did not get anything from input.");
return FALSE;
}
msg = strdup(word_eol[3]);
if (!msg) {
debug("Did not get anything from input.");
return FALSE;
}
debug("setting callback... library will call on_new_query_resolved_cmd_msg() after this");
silc_client_get_clients_whois(silky->client, silky->conn, msgnick, NULL, NULL, on_new_msg_resolved_cmd_msg, msg);
return 2;
}
/* /set command */
static int
cmd_set(char *tbuf, char *word[], char *word_eol[]) {
gchar *attr = word[2], *attr_val = word_eol[3];
gchar *val = NULL;
debug_info("attr = '%s', attr_val = '%s'", attr, attr_val);
if ( !g_utf8_strlen(attr, -1) ) {
/* just list all attributes and abort */
prefs_show_to_console();
return 2;
}
if( !(val = prefs_get(attr)) ) {
printconsole(g_strdup_printf(_("Unknown option '%s'."), attr));
return 2;
}
/* we now know that attr is a valid pref. option */
if( !g_utf8_strlen(attr_val, -1) ) {
printconsole(g_strdup_printf("%s = %s", attr, val));
return 2;
}
switch(prefs_widget_type(attr)) {
case WIDGET_CHECKBUTTON:
/* sanity check, checkbuttons only eat boolean values */
if( g_ascii_strcasecmp(attr_val, "true") && g_ascii_strcasecmp(attr_val, "false") &&
g_ascii_strcasecmp(attr_val, "yes") && g_ascii_strcasecmp(attr_val, "no") &&
g_ascii_strcasecmp(attr_val, "on") && g_ascii_strcasecmp(attr_val, "off") &&
g_ascii_strcasecmp(attr_val, "1") && g_ascii_strcasecmp(attr_val, "0") &&
g_ascii_strcasecmp(attr_val, "toggle") ) {
printconsole(g_strdup_printf("Valid options are 'ON', 'OFF' and 'TOGGLE' for this variable."));
debug("invalid value for boolean variable, not setting '%s'", attr);
return 2;
}
/* adjust attr_val if user wants to toggle */
if( !g_ascii_strcasecmp(attr_val, "toggle") ) {
debug("toggling value ('%s') of '%s'", val, attr);
attr_val = prefs_bool_to_str( !prefs_str_to_bool(val) );
}
}
if( !prefs_set(attr, attr_val) ) {
debug("Could not set '%s' to '%s'", attr, attr_val);
/* FIXME: i18n this after 0.5.1 */
printconsole(g_strdup_printf("Could not set '%s' to '%s'", attr, attr_val));
return 2;
}
printconsole(g_strdup_printf(_("Set %s to %s"), attr, attr_val));
return 2;
} /* End of cmd_set() */
/*
LIST
*/
static int
cmd_list(char *tbuf, char *word[], char *word_eol[]) {
gint rc;
debug_info("cmd_list()");
silc_client_command_call(silky->client, silky->conn, NULL, "LIST", NULL);
rc = TRUE;
return(rc);
} /* End of cmd_list() */
/*
WATCH
*/
static int
cmd_watch(char *tbuf, char *word[], char *word_eol[]) {
SilcBuffer idp;
debug_info("cmd_watch(%s %s)", word[2], word[3]);
if ( ( !strcmp(word[2], "") || !strcmp(word[3], "") ) ) {
debug("empty or missing parameters");
return FALSE;
}
idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT);
if (!strcmp(word[2], "-add")) {
watch_add(word[3]);
debug("added");
return TRUE;
}
if (!strcmp(word[2], "-del")) {
watch_del(word[3]);
debug("removed");
return TRUE;
}
debug("incorrect second parameter");
return FALSE;
}
/*
GETKEY
*/
static int
cmd_getkey(char *tbuf, char *word[], char *word_eol[]) {
int rc;
debug_info("cmd_getkey(%s)", word[2]);
if ( !strcmp(word[2], "") ) { /* fixme: safe? */
rc = FALSE;
}
else {
silc_client_command_call(silky->client, silky->conn, NULL, "GETKEY", word[2], NULL);
rc = TRUE;
}
return(rc);
}
/*
WHOIS nickname OR WHOIS -pubkey keyfile
*/
static int
cmd_whois(char *tbuf, char *word[], char *word_eol[]) {
int rc;
debug_info("cmd_whois(%s %s)", word[2], word[3]);
if ( !strcmp(word[2], "") ) { /* fixme: safe? */
/* empty */
rc = FALSE;
} else if ( !strcmp(word[2], "-pubkey") && strcmp(word[3], "") ) {
debug("doing whois by public key file");
/* -pubkey filename */
silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", word[2], word[3], NULL);
rc = TRUE;
}
else {
/* nickname */
debug("doing whois by nickname");
silc_client_command_call(silky->client, silky->conn, NULL, "WHOIS", word[2], NULL);
rc = TRUE;
}
return(rc);
} /* End of cmd_whois() */
/* *MUST* be kept perfectly sorted for the bsearch to work */
const struct commands xc_cmds[] = {
/* format: "COMMAND", function, connection needed (0|1), need to be on a channel (0|1), help text */
/****************** Not Implemented. ********************
{"ALIAS",
cmd_alias, 0, 0, N_("ALIAS [<name> [<body>]]")},
{"AWAY",
cmd_away, 1, 0, N_("AWAY [<reason>], sets you away")},
{"BAN", cmd_ban, 1, 1,
N_("BAN <mask> [<bantype>], bans everyone matching the"
"mask from the current channel."
"If they are already on the channel this doesn't kick "
"them (needs chanop)")},
**** End of non-implemented commands ***************/
{"CLEAR",
cmd_clear, 0, 0, N_("CLEAR, "
"Clears the current text window")},
/************** Not Implemented. ********************
{"CLOSE",
cmd_close, 0, 0, N_("CLOSE, "
"Closes the current window/tab")},
{"CYCLE",
cmd_cycle, 1, 1, N_("CYCLE, "
"parts current channel and immediately rejoins")},
{"DEOP", cmd_deop, 1, 1, N_("DEOP <nick>, "
"removes chanop status from the nick on the current "
"channel (needs chanop)")},
**** End of non-implemented commands ***************/
{"DISCONNECT", cmd_disconnect, 1, 0, N_("DISCONNECT, "
"Disconnects from server")},
{"GETKEY", cmd_getkey, 1, 0, N_("GETKEY <user> Requests for public key of the user.")},
{"HELP", cmd_help, 0, 0, 0},
/**************** Not Implemented. ********************
{"IGNORE", cmd_ignore, 0, 0,
N_("IGNORE <mask> <types..> <options..>\n"
" mask - host mask to ignore, eg: *!*@*.aol.com\n"
" types - types of data to ignore, one or all of:\n"
" PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
" options - NOSAVE, QUIET")},
**** End of non-implemented commands *****************/
{"JOIN", cmd_join, 1, 0, N_("JOIN <channel>, "
"joins the channel")},
/**************** Not Implemented. ********************
{"KICK", cmd_kick, 1, 1, N_("KICK <nick>, "
"kicks the nick from the current channel (needs chanop)")},
{"KICKBAN", cmd_kickban, 1, 1, N_("KICKBAN <nick>, "
"bans then kicks the nick from the current channel "
"(needs chanop)")},
**** End of non-implemented commands *****************/
{"LIST", cmd_list, 1, 0,
N_("LIST, Lists the channels on the server.")},
{"ME", cmd_me, 1, 0, N_("ME <action>, "
"sends the action to the current channel "
"(actions are written in 3rd person, like "
"/me jumps)")},
{"MSG", cmd_msg, 1, 0, N_("MSG <nickname> <message>, "
"sends a private message to nickname")},
{"NICK", cmd_nick, 0, 0, N_("NICK <nickname>, "
"sets your nick")},
/*************** Not Implemented. ********************
{"NOTIFY", cmd_notify, 0, 0, N_("NOTIFY [<nick>], "
"lists your notify list or adds someone to it")},
{"OP", cmd_op, 1, 1, N_("OP <nick>, "
"gives chanop status to the nick (needs chanop)")},
**** End of non-implemented commands ****************/
{"PART", cmd_part, 1, 1, N_("PART [<channel>], "
"leaves the channel, by default the current one")},
// {"QUERY", cmd_query, 1, 0, N_("QUERY <nickname>, "
{"QUERY", cmd_msg, 1, 0, N_("QUERY <nickname>, "
"opens up a new private conversation window with someone")},
{"QUIT", cmd_quit, 0, 0, N_("QUIT [<reason>], "
"disconnects from the current server, if any and quits Silky")},
/* temporary command, should be commented out from release version! */
{"RAW", cmd_raw, 0, 0, N_("RAW <command> [arguments], "
"sends given SILC command to the server")},
/**************** Not Implemented. ********************
{"RECONNECT", cmd_reconnect, 0, 0, N_("RECONNECT "
"[<host>] [<port>] [<password>], Can be called just as "
"/RECONNECT to reconnect to the current server or "
"with /RECONNECT ALL to reconnect to all the "
"open servers")}
{"SAY", cmd_say, 0, 0, N_("SAY <text>, "
"sends the text to the object in the current window")},
{"SERVCHAN", cmd_servchan, 0, 0, N_("SERVCHAN <host> <port> "
"<channel>, connects and joins a channel")},
**** End of non-implemented commands *****************/
{"SERVER", cmd_server, 0, 0, N_("SERVER <host> [<port>] "
"[<password>], connects to a server, "
"the default port is 706")},
{"SET", cmd_set, 0, 0, N_("SET [<variable> [<value>]] Gets or sets a config variable") },
/**************** Not Implemented. ********************
{"TOPIC", cmd_topic, 1, 1, N_("TOPIC [<topic>], "
"sets the topic if one is given, "
"else shows the current topic")},
{"UNBAN", cmd_unban, 1, 1, N_("UNBAN <mask> "
"[<mask>...], unbans the specified masks.")},
{"UNIGNORE", cmd_unignore, 0, 0, N_("UNIGNORE <mask> "
"[QUIET]")},
**** End of non-implemented commands *****************/
{"WATCH", cmd_watch, 1, 0, N_("WATCH -add <nickname>|-del <nickname> Add or remove user to watch list.") },
{"WHOIS", cmd_whois, 1, 0, N_("WHOIS <nickname> Shows available information of given user.") },
{0, 0, 0, 0, 0} /* End of list. */
};
/* this function is necessary for old bsearch-way of command parsing. leaving
it in place for now, until we decide (see below)
*/
static int
command_compare (const void *a, const void *b)
{
return strcasecmp (a, ((struct commands *)b)->name);
}
static struct commands *find_internal_command (char *name)
{
int i = 0, j, diff;
while (xc_cmds[i].name) {
diff = 0;
for (j=0;j<strlen(xc_cmds[i].name) && j<strlen(name);j++)
if (tolower(xc_cmds[i].name[j]) != tolower(name[j])) diff++;
if (!diff) break;
i++;
}
/* keeping the old way here until we decide which way commands will be parsed */
/* the "-1" is to skip the NULL terminator */
if (xc_cmds[i].name && (strlen(xc_cmds[i].name) >= strlen(name)))
return bsearch (xc_cmds[i].name, xc_cmds, (sizeof (xc_cmds) /
sizeof (xc_cmds[0])) - 1, sizeof (xc_cmds[0]), command_compare);
return(NULL);
}
void
command_help (gchar *tbuf, gchar *helpcmd, int quiet)
{
struct commands *cmd;
/*if (plugin_show_help (sess, helpcmd))
return;*/
cmd = find_internal_command (helpcmd);
if (cmd)
{
if (cmd->help)
{
g_snprintf (tbuf, 255, (_("Usage: %s")), (_(cmd->help)));
printconsole (tbuf);
} else
{
if (!quiet)
printconsole (_("No help available for that command."));
}
return;
}
if (!quiet)
printconsole (_("No such command."));
}
gboolean handle_command_callback (GtkWidget *emitter, gchar *cmd) {
debug("handle_command_callback('%s')", cmd);
handle_command(cmd);
return TRUE;
}
/* handle a command, without the '/' prefix */
int
handle_command (char *cmd)
{
/*struct popup *pop;
int user_cmd = FALSE;*/
/*GSList *list;*/
char *word[PDIWORDS];
char *word_eol[PDIWORDS];
static int command_level = 0;
struct commands *int_cmd;
char pdibuf_static[1024];
char tbuf_static[4096];
char *pdibuf;
char *tbuf;
int len;
int ret = TRUE;
debug_info("cmd: '%s'", cmd);
/* anything below MUST DEC command_level before returning */
len = strlen (cmd);
if (len == 0)
return FALSE;
if (len >= sizeof (pdibuf_static))
pdibuf = malloc (len + 1);
else
pdibuf = pdibuf_static;
if ((len * 2) >= sizeof (tbuf_static))
tbuf = malloc ((len * 2) + 1);
else
tbuf = tbuf_static;
/* split the text into words and word_eol */
process_data_init (pdibuf, cmd, word, word_eol, TRUE);
/* first see if it's a userCommand */
/* list = command_list; No user commands yet
while (list)
{
pop = (struct popup *) list->data;
if (!strcasecmp (pop->name, word[1]))
{
user_command (sess, tbuf, pop->cmd, word, word_eol);
user_cmd = TRUE;
}
list = list->next;
}
if (user_cmd)
goto xit;
*/
/* now check internal commands */
int_cmd = find_internal_command (word[1]);
if (int_cmd) {
if (int_cmd->needserver && !CONNECTED) {
notc_msg (); /* not connected to a server */
} else if ( int_cmd->needchannel && !channels ) { /* FIXME, check that user is atleast on one channel */
notj_msg (); /* not joined to a channel */
} else {
switch (int_cmd->callback (tbuf, word, word_eol)) {
case FALSE:
command_help (tbuf, int_cmd->name, TRUE);
break;
case 2:
ret = FALSE;
goto xit;
}
}
} else {
debug_info("Unknown command '%s'", word[1] );
printconsole(_("No such command."));
}
xit:
command_level--;
if (pdibuf != pdibuf_static)
free (pdibuf);
if (tbuf != tbuf_static)
free (tbuf);
return ret;
}
void
process_data_init (char *buf, char *cmd, char *word[],
char *word_eol[], int handle_quotes)
{
int wordcount = 2;
int space = FALSE;
int quote = FALSE;
int j = 0;
int len;
word[0] = "\000\000";
word_eol[0] = "\000\000";
word[1] = (char *)buf;
word_eol[1] = (char *)cmd;
while (1)
{
switch (*cmd)
{
case 0:
jump:
buf[j] = 0;
for (j = wordcount; j < PDIWORDS; j++)
{
word[j] = "\000\000";
word_eol[j] = "\000\000";
}
return;
case '\042':
if (!handle_quotes)
goto def;
if (quote)
quote = FALSE;
else
quote = TRUE;
cmd++;
break;
case ' ':
if (!quote)
{
if (!space)
{
buf[j] = 0;
j++;
word[wordcount] = &buf[j];
word_eol[wordcount] = cmd + 1;
wordcount++;
if (wordcount == PDIWORDS - 1)
goto jump;
space = TRUE;
}
cmd++;
break;
}
default:
def:
space = FALSE;
len = g_utf8_skip[((unsigned char *)cmd)[0]];
if (len == 1)
{
buf[j] = *cmd;
j++;
cmd++;
} else
{
/* skip past a multi-byte utf8 char */
memcpy (buf + j, cmd, len);
j += len;
cmd += len;
}
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1