You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hexchat/src/common/outbound.c

4875 lines
103 KiB
C

/* X-Chat
* Copyright (C) 1998 Peter Zelezny.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define _GNU_SOURCE /* for memrchr */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#define WANTSOCKET
#define WANTARPA
#include "inet.h"
#ifndef WIN32
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <time.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "hexchat.h"
#include "plugin.h"
#include "ignore.h"
#include "util.h"
#include "fe.h"
#include "cfgfiles.h" /* hexchat_fopen_file() */
#include "network.h" /* net_ip() */
#include "modes.h"
#include "notify.h"
#include "inbound.h"
#include "text.h"
#include "hexchatc.h"
#include "servlist.h"
#include "server.h"
#include "tree.h"
#include "outbound.h"
#include "chanopt.h"
#define TBUFSIZE 4096
static void help (session *sess, char *tbuf, char *helpcmd, int quiet);
static int cmd_server (session *sess, char *tbuf, char *word[], char *word_eol[]);
static void handle_say (session *sess, char *text, int check_spch);
static void
notj_msg (struct session *sess)
{
PrintText (sess, _("No channel joined. Try /join #<channel>\n"));
}
void
notc_msg (struct session *sess)
{
PrintText (sess, _("Not connected. Try /server <host> [<port>]\n"));
}
static char *
random_line (char *file_name)
{
FILE *fh;
char buf[512];
int lines, ran;
if (!file_name[0])
goto nofile;
fh = hexchat_fopen_file (file_name, "r", 0);
if (!fh)
{
nofile:
/* reason is not a file, an actual reason! */
return g_strdup (file_name);
}
/* count number of lines in file */
lines = 0;
while (fgets (buf, sizeof (buf), fh))
lines++;
if (lines < 1)
goto nofile;
/* go down a random number */
rewind (fh);
ran = RAND_INT (lines);
do
{
fgets (buf, sizeof (buf), fh);
lines--;
}
while (lines > ran);
fclose (fh);
return g_strdup (buf);
}
void
server_sendpart (server * serv, char *channel, char *reason)
{
if (!reason)
{
reason = random_line (prefs.hex_irc_part_reason);
serv->p_part (serv, channel, reason);
g_free (reason);
} else
{
/* reason set by /quit, /close argument */
serv->p_part (serv, channel, reason);
}
}
void
server_sendquit (session * sess)
{
char *rea, *colrea;
if (!sess->quitreason)
{
colrea = g_strdup (prefs.hex_irc_quit_reason);
check_special_chars (colrea, FALSE);
rea = random_line (colrea);
g_free (colrea);
sess->server->p_quit (sess->server, rea);
g_free (rea);
} else
{
/* reason set by /quit, /close argument */
sess->server->p_quit (sess->server, sess->quitreason);
}
}
void
process_data_init (char *buf, char *cmd, char *word[],
char *word_eol[], gboolean handle_quotes,
gboolean allow_escape_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:
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;
/* two quotes turn into 1 */
if (allow_escape_quotes && cmd[1] == '\042')
{
cmd++;
goto def;
}
if (quote)
{
quote = FALSE;
space = FALSE;
} else
quote = TRUE;
cmd++;
break;
case ' ':
if (!quote)
{
if (!space)
{
buf[j] = 0;
j++;
if (wordcount < PDIWORDS)
{
word[wordcount] = &buf[j];
word_eol[wordcount] = cmd + 1;
wordcount++;
}
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;
}
}
}
}
static int
cmd_addbutton (struct session *sess, char *tbuf, char *word[],
char *word_eol[])
{
if (*word[2] && *word_eol[3])
{
if (sess->type == SESS_DIALOG)
{
list_addentry (&dlgbutton_list, word_eol[3], word[2]);
fe_dlgbuttons_update (sess);
} else
{
list_addentry (&button_list, word_eol[3], word[2]);
fe_buttons_update (sess);
}
return TRUE;
}
return FALSE;
}
/* ADDSERVER <networkname> <serveraddress>, add a new network and server to the network list */
static int
cmd_addserver (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
ircnet *network;
/* do we have enough arguments given? */
if (*word[2] && *word_eol[3])
{
network = servlist_net_find (word[2], NULL, strcmp);
/* if the given network doesn't exist yet, add it */
if (!network)
{
network = servlist_net_add (word[2], "", TRUE);
network->encoding = g_strdup (IRC_DEFAULT_CHARSET);
}
/* if we had the network already, check if the given server already exists */
else if (servlist_server_find (network, word_eol[3], NULL))
{
PrintTextf (sess, _("Server %s already exists on network %s.\n"), word_eol[3], word[2]);
return TRUE; /* unsuccessful, but the syntax was correct so we don't want to show the help */
}
/* server added to new or existing network, doesn't make any difference */
servlist_server_add (network, word_eol[3]);
PrintTextf (sess, _("Added server %s to network %s.\n"), word_eol[3], word[2]);
return TRUE; /* success */
}
else
{
return FALSE; /* print help */
}
}
static int
cmd_allchannels (session *sess, char *tbuf, char *word[], char *word_eol[])
{
GSList *list = sess_list;
if (!*word_eol[2])
return FALSE;
while (list)
{
sess = list->data;
if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->connected)
{
handle_command (sess, word_eol[2], FALSE);
}
list = list->next;
}
return TRUE;
}
static int
cmd_allchannelslocal (session *sess, char *tbuf, char *word[], char *word_eol[])
{
GSList *list = sess_list;
server *serv = sess->server;
if (!*word_eol[2])
return FALSE;
while (list)
{
sess = list->data;
if (sess->type == SESS_CHANNEL && sess->channel[0] &&
sess->server->connected && sess->server == serv)
{
handle_command (sess, word_eol[2], FALSE);
}
list = list->next;
}
return TRUE;
}
static int
cmd_allservers (struct session *sess, char *tbuf, char *word[],
char *word_eol[])
{
GSList *list;
server *serv;
if (!*word_eol[2])
return FALSE;
list = serv_list;
while (list)
{
serv = list->data;
if (serv->connected)
handle_command (serv->front_session, word_eol[2], FALSE);
list = list->next;
}
return TRUE;
}
static int
cmd_away (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *reason = word_eol[2];
if (!(*reason))
{
if (sess->server->is_away)
{
if (sess->server->last_away_reason)
PrintTextf (sess, _("Already marked away: %s\n"), sess->server->last_away_reason);
return FALSE;
}
if (sess->server->reconnect_away)
reason = sess->server->last_away_reason;
else
/* must manage memory pointed to by random_line() */
reason = random_line (prefs.hex_away_reason);
}
sess->server->p_set_away (sess->server, reason);
if (sess->server->last_away_reason != reason)
{
g_free (sess->server->last_away_reason);
if (reason == word_eol[2])
sess->server->last_away_reason = g_strdup (reason);
else
sess->server->last_away_reason = reason;
}
if (!sess->server->connected)
sess->server->reconnect_away = 1;
return TRUE;
}
static int
cmd_back (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (sess->server->is_away)
{
sess->server->p_set_back (sess->server);
}
else
{
PrintText (sess, _("Already marked back.\n"));
}
g_free (sess->server->last_away_reason);
sess->server->last_away_reason = NULL;
return TRUE;
}
static char *
create_mask (session * sess, char *mask, char *mode, char *typestr, int deop)
{
int type;
struct User *user;
char *at, *dot, *lastdot;
char username[64], fullhost[128], domain[128], buf[512], *p2;
user = userlist_find (sess, mask);
if (user && user->hostname) /* it's a nickname, let's find a proper ban mask */
{
if (deop)
p2 = user->nick;
else
p2 = "";
mask = user->hostname;
at = strchr (mask, '@'); /* FIXME: utf8 */
if (!at)
return NULL; /* can't happen? */
*at = 0;
if (mask[0] == '~' || mask[0] == '+' ||
mask[0] == '=' || mask[0] == '^' || mask[0] == '-')
{
/* the ident is prefixed with something, we replace that sign with an * */
safe_strcpy (username+1, mask+1, sizeof (username)-1);
username[0] = '*';
} else if (at - mask < USERNAMELEN)
{
/* we just add an * in the begining of the ident */
safe_strcpy (username+1, mask, sizeof (username)-1);
username[0] = '*';
} else
{
/* ident might be too long, we just ban what it gives and add an * in the end */
safe_strcpy (username, mask, sizeof (username));
}
*at = '@';
safe_strcpy (fullhost, at + 1, sizeof (fullhost));
dot = strchr (fullhost, '.');
if (dot)
{
safe_strcpy (domain, dot, sizeof (domain));
} else
{
safe_strcpy (domain, fullhost, sizeof (domain));
}
if (*typestr)
type = atoi (typestr);
else
type = prefs.hex_irc_ban_type;
buf[0] = 0;
if (inet_addr (fullhost) != -1) /* "fullhost" is really a IP number */
{
lastdot = strrchr (fullhost, '.');
if (!lastdot)
return NULL; /* can't happen? */
*lastdot = 0;
strcpy (domain, fullhost);
*lastdot = '.';
switch (type)
{
case 0:
g_snprintf (buf, sizeof (buf), "%s %s *!*@%s.*", mode, p2, domain);
break;
case 1:
g_snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
break;
case 2:
g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s.*", mode, p2, username, domain);
break;
case 3:
g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
break;
}
} else
{
switch (type)
{
case 0:
g_snprintf (buf, sizeof (buf), "%s %s *!*@*%s", mode, p2, domain);
break;
case 1:
g_snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost);
break;
case 2:
g_snprintf (buf, sizeof (buf), "%s %s *!%s@*%s", mode, p2, username, domain);
break;
case 3:
g_snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost);
break;
}
}
} else
{
g_snprintf (buf, sizeof (buf), "%s %s", mode, mask);
}
return g_strdup (buf);
}
static void
ban (session * sess, char *tbuf, char *mask, char *bantypestr, int deop)
{
char *banmask = create_mask (sess, mask, deop ? "-o+b" : "+b", bantypestr, deop);
server *serv = sess->server;
if (banmask)
{
serv->p_mode (serv, sess->channel, banmask);
g_free (banmask);
}
}
static int
cmd_ban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *mask = word[2];
if (*mask)
{
ban (sess, tbuf, mask, word[3], 0);
} else
{
sess->server->p_mode (sess->server, sess->channel, "+b"); /* banlist */
}
return TRUE;
}
static int
cmd_unban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
/* Allow more than one mask in /unban -- tvk */
int i = 2;
while (1)
{
if (!*word[i])
{
if (i == 2)
return FALSE;
send_channel_modes (sess, tbuf, word, 2, i, '-', 'b', 0);
return TRUE;
}
i++;
}
}
static int
cmd_chanopt (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int ret;
/* chanopt.c */
ret = chanopt_command (sess, tbuf, word, word_eol);
chanopt_save_all ();
return ret;
}
static int
cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
server *serv = sess->server;
int offset = 0;
if (strcmp (word[2], "-quiet") == 0)
offset++;
if (!word[2 + offset][0])
{
PrintTextf (sess, "Current charset: %s\n", serv->encoding);
return TRUE;
}
if (servlist_check_encoding (word[2 + offset]))
{
server_set_encoding (serv, word[2 + offset]);
if (offset < 1)
PrintTextf (sess, "Charset changed to: %s\n", word[2 + offset]);
} else
{
PrintTextf (sess, "\0034Unknown charset:\017 %s\n", word[2 + offset]);
}
return TRUE;
}
static int
cmd_clear (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
GSList *list = sess_list;
char *reason = word_eol[2];
if (g_ascii_strcasecmp (reason, "HISTORY") == 0)
{
history_free (&sess->history);
return TRUE;
}
if (g_ascii_strncasecmp (reason, "all", 3) == 0)
{
while (list)
{
sess = list->data;
if (!sess->nick_said)
fe_text_clear (list->data, 0);
list = list->next;
}
return TRUE;
}
if (reason[0] != '-' && !isdigit (reason[0]) && reason[0] != 0)
return FALSE;
fe_text_clear (sess, atoi (reason));
return TRUE;
}
static int
cmd_close (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
GSList *list;
if (strcmp (word[2], "-m") == 0)
{
list = sess_list;
while (list)
{
sess = list->data;
list = list->next;
if (sess->type == SESS_DIALOG)
fe_close_window (sess);
}
} else
{
if (*word_eol[2])
sess->quitreason = word_eol[2];
fe_close_window (sess);
}
return TRUE;
}
static int
cmd_ctcp (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int mbl;
char *to = word[2];
if (*to)
{
char *msg = word_eol[3];
if (*msg)
{
unsigned char *cmd = (unsigned char *)msg;
/* make the first word upper case (as per RFC) */
while (1)
{
if (*cmd == ' ' || *cmd == 0)
break;
mbl = g_utf8_skip[*cmd];
if (mbl == 1)
*cmd = toupper (*cmd);
cmd += mbl;
}
sess->server->p_ctcp (sess->server, to, msg);
EMIT_SIGNAL (XP_TE_CTCPSEND, sess, to, msg, NULL, NULL, 0);
return TRUE;
}
}
return FALSE;
}
static int
cmd_country (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *code = word[2];
if (*code)
{
/* search? */
if (strcmp (code, "-s") == 0)
{
country_search (word[3], sess, (void *)PrintTextf);
return TRUE;
}
/* search, but forgot the -s */
if (strchr (code, '*'))
{
country_search (code, sess, (void *)PrintTextf);
return TRUE;
}
sprintf (tbuf, "%s = %s\n", code, country (code));
PrintText (sess, tbuf);
return TRUE;
}
return FALSE;
}
static int
cmd_cycle (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *key = NULL;
char *chan = word[2];
session *chan_sess;
if (!*chan)
chan = sess->channel;
if (chan)
{
chan_sess = find_channel (sess->server, chan);
if (chan_sess && chan_sess->type == SESS_CHANNEL)
{
key = chan_sess->channelkey;
sess->server->p_cycle (sess->server, chan, key);
return TRUE;
}
}
return FALSE;
}
static int
cmd_dcc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int goodtype;
struct DCC *dcc = 0;
char *type = word[2];
if (*type)
{
if (!g_ascii_strcasecmp (type, "HELP"))
return FALSE;
if (!g_ascii_strcasecmp (type, "CLOSE"))
{
if (*word[3] && *word[4])
{
goodtype = 0;
if (!g_ascii_strcasecmp (word[3], "SEND"))
{
dcc = find_dcc (word[4], word[5], TYPE_SEND);
dcc_abort (sess, dcc);
goodtype = TRUE;
}
if (!g_ascii_strcasecmp (word[3], "GET"))
{
dcc = find_dcc (word[4], word[5], TYPE_RECV);
dcc_abort (sess, dcc);
goodtype = TRUE;
}
if (!g_ascii_strcasecmp (word[3], "CHAT"))
{
dcc = find_dcc (word[4], "", TYPE_CHATRECV);
if (!dcc)
dcc = find_dcc (word[4], "", TYPE_CHATSEND);
dcc_abort (sess, dcc);
goodtype = TRUE;
}
if (!goodtype)
return FALSE;
if (!dcc)
EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
return TRUE;
}
return FALSE;
}
if ((!g_ascii_strcasecmp (type, "CHAT")) || (!g_ascii_strcasecmp (type, "PCHAT")))
{
char *nick = word[3];
int passive = (!g_ascii_strcasecmp(type, "PCHAT")) ? 1 : 0;
if (*nick)
dcc_chat (sess, nick, passive);
return TRUE;
}
if (!g_ascii_strcasecmp (type, "LIST"))
{
dcc_show_list (sess);
return TRUE;
}
if (!g_ascii_strcasecmp (type, "GET"))
{
char *nick = word[3];
char *file = word[4];
if (!*file)
{
if (*nick)
dcc_get_nick (sess, nick);
} else
{
dcc = find_dcc (nick, file, TYPE_RECV);
if (dcc)
dcc_get (dcc);
else
EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
}
return TRUE;
}
if ((!g_ascii_strcasecmp (type, "SEND")) || (!g_ascii_strcasecmp (type, "PSEND")))
{
int i = 3, maxcps;
char *nick, *file;
int passive = (!g_ascii_strcasecmp(type, "PSEND")) ? 1 : 0;
nick = word[i];
if (!*nick)
return FALSE;
maxcps = prefs.hex_dcc_max_send_cps;
if (!g_ascii_strncasecmp(nick, "-maxcps=", 8))
{
maxcps = atoi(nick + 8);
i++;
nick = word[i];
if (!*nick)
return FALSE;
}
i++;
file = word[i];
if (!*file)
{
fe_dcc_send_filereq (sess, nick, maxcps, passive);
return TRUE;
}
do
{
dcc_send (sess, nick, file, maxcps, passive);
i++;
file = word[i];
}
while (*file);
return TRUE;
}
return FALSE;
}
dcc_show_list (sess);
return TRUE;
}
static int
cmd_debug (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
struct session *s;
struct server *v;
GSList *list = sess_list;
PrintText (sess, "Session T Channel WaitChan WillChan Server\n");
while (list)
{
s = (struct session *) list->data;
sprintf (tbuf, "%p %1x %-10.10s %-10.10s %-10.10s %p\n",
s, s->type, s->channel, s->waitchannel,
s->willjoinchannel, s->server);
PrintText (sess, tbuf);
list = list->next;
}
list = serv_list;
PrintText (sess, "Server Sock Name\n");
while (list)
{
v = (struct server *) list->data;
sprintf (tbuf, "%p %-5d %s\n",
v, v->sok, v->servername);
PrintText (sess, tbuf);
list = list->next;
}
sprintf (tbuf,
"\nfront_session: %p\n"
"current_tab: %p\n\n",
sess->server->front_session, current_tab);
PrintText (sess, tbuf);
return TRUE;
}
static int
cmd_delbutton (struct session *sess, char *tbuf, char *word[],
char *word_eol[])
{
if (*word[2])
{
if (sess->type == SESS_DIALOG)
{
if (list_delentry (&dlgbutton_list, word[2]))
fe_dlgbuttons_update (sess);
} else
{
if (list_delentry (&button_list, word[2]))
fe_buttons_update (sess);
}
return TRUE;
}
return FALSE;
}
static int
cmd_dehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int i = 2;
while (1)
{
if (!*word[i])
{
if (i == 2)
return FALSE;
send_channel_modes (sess, tbuf, word, 2, i, '-', 'h', 0);
return TRUE;
}
i++;
}
}
static int
cmd_deop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int i = 2;
while (1)
{
if (!*word[i])
{
if (i == 2)
return FALSE;
send_channel_modes (sess, tbuf, word, 2, i, '-', 'o', 0);
return TRUE;
}
i++;
}
}
typedef struct
{
char **nicks;
int i;
session *sess;
char *reason;
char *tbuf;
} multidata;
static int
mdehop_cb (struct User *user, multidata *data)
{
if (user->hop && !user->me)
{
data->nicks[data->i] = user->nick;
data->i++;
}
return TRUE;
}
static int
cmd_mdehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char **nicks = g_new0 (char *, sess->hops);
multidata data;
data.nicks = nicks;
data.i = 0;
tree_foreach (sess->usertree, (tree_traverse_func *)mdehop_cb, &data);
send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'h', 0);
g_free (nicks);
return TRUE;
}
static int
mdeop_cb (struct User *user, multidata *data)
{
if (user->op && !user->me)
{
data->nicks[data->i] = user->nick;
data->i++;
}
return TRUE;
}
static int
cmd_mdeop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char **nicks = g_new0(char *, sess->ops);
multidata data;
data.nicks = nicks;
data.i = 0;
tree_foreach (sess->usertree, (tree_traverse_func *)mdeop_cb, &data);
send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'o', 0);
g_free (nicks);
return TRUE;
}
GSList *menu_list = NULL;
static void
menu_free (menu_entry *me)
{
g_free (me->path);
g_free (me->label);
g_free (me->cmd);
g_free (me->ucmd);
g_free (me->group);
g_free (me->icon);
g_free (me);
}
/* strings equal? but ignore underscores */
int
menu_streq (const char *s1, const char *s2, int def)
{
/* for separators */
if (s1 == NULL && s2 == NULL)
return 0;
if (s1 == NULL || s2 == NULL)
return 1;
while (*s1)
{
if (*s1 == '_')
s1++;
if (*s2 == '_')
s2++;
if (*s1 != *s2)
return 1;
s1++;
s2++;
}
if (!*s2)
return 0;
return def;
}
static menu_entry *
menu_entry_find (char *path, char *label)
{
GSList *list;
menu_entry *me;
list = menu_list;
while (list)
{
me = list->data;
if (!strcmp (path, me->path))
{
if (me->label && label && !strcmp (label, me->label))
return me;
}
list = list->next;
}
return NULL;
}
static void
menu_del_children (char *path, char *label)
{
GSList *list, *next;
menu_entry *me;
char buf[512];
if (!label)
label = "";
if (path[0])
g_snprintf (buf, sizeof (buf), "%s/%s", path, label);
else
g_snprintf (buf, sizeof (buf), "%s", label);
list = menu_list;
while (list)
{
me = list->data;
next = list->next;
if (!menu_streq (buf, me->path, 0))
{
menu_list = g_slist_remove (menu_list, me);
menu_free (me);
}
list = next;
}
}
static int
menu_del (char *path, char *label)
{
GSList *list;
menu_entry *me;
list = menu_list;
while (list)
{
me = list->data;
if (!menu_streq (me->label, label, 1) && !menu_streq (me->path, path, 1))
{
menu_list = g_slist_remove (menu_list, me);
fe_menu_del (me);
menu_free (me);
/* delete this item's children, if any */
menu_del_children (path, label);
return 1;
}
list = list->next;
}
return 0;
}
static char
menu_is_mainmenu_root (char *path, gint16 *offset)
{
static const char *menus[] = {"\x4$TAB","\x5$TRAY","\x4$URL","\x5$NICK","\x5$CHAN"};
int i;
for (i = 0; i < 5; i++)
{
if (!strncmp (path, menus[i] + 1, menus[i][0]))
{
*offset = menus[i][0]; /* number of bytes to offset the root */
if (path[*offset] != '\0')
*offset += 1;
return 0; /* is not main menu */
}
}
*offset = 0;
return 1; /* is main menu */
}
static void
menu_add (char *path, char *label, char *cmd, char *ucmd, int pos, int state, int markup, int enable, int mod, int key, char *group, char *icon)
{
menu_entry *me;
/* already exists? */
me = menu_entry_find (path, label);
if (me)
{
/* update only */
me->state = state;
me->enable = enable;
fe_menu_update (me);
return;
}
me = g_new (menu_entry, 1);
me->pos = pos;
me->modifier = mod;
me->is_main = menu_is_mainmenu_root (path, &me->root_offset);
me->state = state;
me->markup = markup;
me->enable = enable;
me->key = key;
me->path = g_strdup (path);
me->label = NULL;
me->cmd = NULL;
me->ucmd = NULL;
me->group = NULL;
me->icon = NULL;
me->label = g_strdup (label);
me->cmd = g_strdup (cmd);
me->ucmd = g_strdup (ucmd);
me->group = g_strdup (group);
me->icon = g_strdup (icon);
menu_list = g_slist_append (menu_list, me);
label = fe_menu_add (me);
if (label)
{
/* FE has given us a stripped label */
g_free (me->label);
me->label = g_strdup (label);
g_free (label); /* this is from pango */
}
}
static int
cmd_menu (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int idx = 2;
int len;
int pos = 0xffff;
int state = 0;
int toggle = FALSE;
int enable = TRUE;
int markup = FALSE;
int key = 0;
int mod = 0;
char *label;
char *group = NULL;
char *icon = NULL;
if (!word[2][0] || !word[3][0])
return FALSE;
/* -eX enabled or not? */
if (word[idx][0] == '-' && word[idx][1] == 'e')
{
enable = atoi (word[idx] + 2);
idx++;
}
/* -i<ICONFILE> */
if (word[idx][0] == '-' && word[idx][1] == 'i')
{
icon = word[idx] + 2;
idx++;
}
/* -k<mod>,<key> key binding */
if (word[idx][0] == '-' && word[idx][1] == 'k')
{
char *comma = strchr (word[idx], ',');
if (!comma)
return FALSE;
mod = atoi (word[idx] + 2);
key = atoi (comma + 1);
idx++;
}
/* -m to specify PangoMarkup language */
if (word[idx][0] == '-' && word[idx][1] == 'm')
{
markup = TRUE;
idx++;
}
/* -pX to specify menu position */
if (word[idx][0] == '-' && word[idx][1] == 'p')
{
pos = atoi (word[idx] + 2);
idx++;
}
/* -rSTATE,GROUP to specify a radio item */
if (word[idx][0] == '-' && word[idx][1] == 'r')
{
state = atoi (word[idx] + 2);
group = word[idx] + 4;
idx++;
}
/* -tX to specify toggle item with default state */
if (word[idx][0] == '-' && word[idx][1] == 't')
{
state = atoi (word[idx] + 2);
idx++;
toggle = TRUE;
}
if (word[idx+1][0] == 0)
return FALSE;
/* the path */
path_part (word[idx+1], tbuf, 512);
len = strlen (tbuf);
if (len)
tbuf[len - 1] = 0;
/* the name of the item */
label = file_part (word[idx + 1]);
if (label[0] == '-' && label[1] == 0)
label = NULL; /* separator */
if (markup)
{
char *p; /* to force pango closing tags through */
for (p = label; p && *p; p++)
if (*p == 3)
*p = '/';
}
if (!g_ascii_strcasecmp (word[idx], "ADD"))
{
if (toggle)
{
menu_add (tbuf, label, word[idx + 2], word[idx + 3], pos, state, markup, enable, mod, key, NULL, NULL);
} else
{
if (word[idx + 2][0])
menu_add (tbuf, label, word[idx + 2], NULL, pos, state, markup, enable, mod, key, group, icon);
else
menu_add (tbuf, label, NULL, NULL, pos, state, markup, enable, mod, key, group, icon);
}
return TRUE;
}
if (!g_ascii_strcasecmp (word[idx], "DEL"))
{
menu_del (tbuf, label);
return TRUE;
}
return FALSE;
}
static int
mkick_cb (struct User *user, multidata *data)
{
if (!user->op && !user->me)
data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
return TRUE;
}
static int
mkickops_cb (struct User *user, multidata *data)
{
if (user->op && !user->me)
data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
return TRUE;
}
static int
cmd_mkick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
multidata data;
data.sess = sess;
data.reason = word_eol[2];
tree_foreach (sess->usertree, (tree_traverse_func *)mkickops_cb, &data);
tree_foreach (sess->usertree, (tree_traverse_func *)mkick_cb, &data);
return TRUE;
}
static int
cmd_devoice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int i = 2;
while (1)
{
if (!*word[i])
{
if (i == 2)
return FALSE;
send_channel_modes (sess, tbuf, word, 2, i, '-', 'v', 0);
return TRUE;
}
i++;
}
}
static int
cmd_discon (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
sess->server->disconnect (sess, TRUE, -1);
return TRUE;
}
static int
cmd_dns (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *nick = word[2];
struct User *user;
message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
if (*nick)
{
user = userlist_find (sess, nick);
if (user)
{
if (user->hostname)
{
do_dns (sess, user->nick, user->hostname, &no_tags);
} else
{
sess->server->p_get_ip (sess->server, nick);
sess->server->doing_dns = TRUE;
}
} else
{
do_dns (sess, NULL, nick, &no_tags);
}
return TRUE;
}
return FALSE;
}
static int
cmd_echo (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
PrintText (sess, word_eol[2]);
return TRUE;
}
#ifndef WIN32
static void
exec_check_process (struct session *sess)
{
int val;
if (sess->running_exec == NULL)
return;
val = waitpid (sess->running_exec->childpid, NULL, WNOHANG);
if (val == -1 || val > 0)
{
close (sess->running_exec->myfd);
fe_input_remove (sess->running_exec->iotag);
g_free (sess->running_exec);
sess->running_exec = NULL;
}
}
#ifndef __EMX__
static int
cmd_execs (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int r;
exec_check_process (sess);
if (sess->running_exec == NULL)
{
EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
return FALSE;
}
r = kill (sess->running_exec->childpid, SIGSTOP);
if (r == -1)
PrintText (sess, "Error in kill(2)\n");
return TRUE;
}
static int
cmd_execc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int r;
exec_check_process (sess);
if (sess->running_exec == NULL)
{
EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
return FALSE;
}
r = kill (sess->running_exec->childpid, SIGCONT);
if (r == -1)
PrintText (sess, "Error in kill(2)\n");
return TRUE;
}
static int
cmd_execk (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int r;
exec_check_process (sess);
if (sess->running_exec == NULL)
{
EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
return FALSE;
}
if (strcmp (word[2], "-9") == 0)
r = kill (sess->running_exec->childpid, SIGKILL);
else
r = kill (sess->running_exec->childpid, SIGTERM);
if (r == -1)
PrintText (sess, "Error in kill(2)\n");
return TRUE;
}
/* OS/2 Can't have the /EXECW command because it uses pipe(2) not socketpair
and thus it is simplex --AGL */
static int
cmd_execw (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int len;
char *temp;
exec_check_process (sess);
if (sess->running_exec == NULL)
{
EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
return FALSE;
}
len = strlen(word_eol[2]);
temp = g_strconcat (word_eol[2], "\n", NULL);
PrintText(sess, temp);
write(sess->running_exec->myfd, temp, len + 1);
g_free(temp);
return TRUE;
}
#endif /* !__EMX__ */
/* convert ANSI escape color codes to mIRC codes */
static short escconv[] =
/* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 */
{ 1,4,3,5,2,10,6,1, 1,7,9,8,12,11,13,1 };
static void
exec_handle_colors (char *buf, int len)
{
char numb[16];
char *nbuf;
int i = 0, j = 0, k = 0, firstn = 0, col, colf = 0, colb = 0;
int esc = FALSE, backc = FALSE, bold = FALSE;
/* any escape codes in this text? */
if (strchr (buf, 27) == 0)
return;
nbuf = g_malloc (len + 1);
while (i < len)
{
switch (buf[i])
{
case '\r':
break;
case 27:
esc = TRUE;
break;
case ';':
if (!esc)
goto norm;
backc = TRUE;
numb[k] = 0;
firstn = atoi (numb);
k = 0;
break;
case '[':
if (!esc)
goto norm;
break;
default:
if (esc)
{
if (buf[i] >= 'A' && buf[i] <= 'z')
{
if (buf[i] == 'm')
{
/* ^[[0m */
if (k == 0 || (numb[0] == '0' && k == 1))
{
nbuf[j] = '\017';
j++;
bold = FALSE;
goto cont;
}
numb[k] = 0;
col = atoi (numb);
backc = FALSE;
if (firstn == 1)
bold = TRUE;
if (firstn >= 30 && firstn <= 37)
colf = firstn - 30;
if (col >= 40)
{
colb = col - 40;
backc = TRUE;
}
if (col >= 30 && col <= 37)
colf = col - 30;
if (bold)
colf += 8;
if (backc)
{
colb = escconv[colb % 14];
colf = escconv[colf % 14];
j += sprintf (&nbuf[j], "\003%d,%02d", colf, colb);
} else
{
colf = escconv[colf % 14];
j += sprintf (&nbuf[j], "\003%02d", colf);
}
}
cont: esc = FALSE;
backc = FALSE;
k = 0;
} else
{
if (isdigit ((unsigned char) buf[i]) && k < (sizeof (numb) - 1))
{
numb[k] = buf[i];
k++;
}
}
} else
{
norm: nbuf[j] = buf[i];
j++;
}
}
i++;
}
nbuf[j] = 0;
memcpy (buf, nbuf, j + 1);
g_free (nbuf);
}
#ifndef HAVE_MEMRCHR
static void *
memrchr (const void *block, int c, size_t size)
{
unsigned char *p;
for (p = (unsigned char *)block + size; p != block; p--)
if (*p == c)
return p;
return NULL;
}
#endif
static gboolean
exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
{
char *buf, *readpos, *rest;
int rd, len;
int sok = s->myfd;
len = s->buffill;
if (len) {
/* append new data to buffered incomplete line */
buf = g_malloc (len + 2050);
memcpy(buf, s->linebuf, len);
readpos = buf + len;
g_free (s->linebuf);
s->linebuf = NULL;
}
else
readpos = buf = g_malloc (2050);
rd = read (sok, readpos, 2048);
if (rd < 1)
{
/* The process has died */
kill(s->childpid, SIGKILL);
if (len) {
buf[len] = '\0';
exec_handle_colors(buf, len);
if (s->tochannel)
{
/* must turn off auto-completion temporarily */
unsigned int old = prefs.hex_completion_auto;
prefs.hex_completion_auto = 0;
handle_multiline (s->sess, buf, FALSE, TRUE);
prefs.hex_completion_auto = old;
}
else
PrintText (s->sess, buf);
}
g_free(buf);
waitpid (s->childpid, NULL, 0);
s->sess->running_exec = NULL;
fe_input_remove (s->iotag);
close (sok);
g_free (s);
return TRUE;
}
len += rd;
buf[len] = '\0';
rest = memrchr(buf, '\n', len);
if (rest)
rest++;
else
rest = buf;
if (*rest) {
s->buffill = len - (rest - buf); /* = strlen(rest) */
s->linebuf = g_malloc (s->buffill + 1);
memcpy(s->linebuf, rest, s->buffill);
*rest = '\0';
len -= s->buffill; /* possibly 0 */
}
else
s->buffill = 0;
if (len) {
exec_handle_colors (buf, len);
if (s->tochannel)
handle_multiline (s->sess, buf, FALSE, TRUE);
else
PrintText (s->sess, buf);
}
g_free (buf);
return TRUE;
}
static int
cmd_exec (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int tochannel = FALSE;
char *cmd = word_eol[2];
int fds[2], pid = 0;
struct nbexec *s;
int shell = TRUE;
int fd;
if (*cmd)
{
exec_check_process (sess);
if (sess->running_exec != NULL)
{
EMIT_SIGNAL (XP_TE_ALREADYPROCESS, sess, NULL, NULL, NULL, NULL, 0);
return TRUE;
}
if (!strcmp (word[2], "-d"))
{
if (!*word[3])
return FALSE;
cmd = word_eol[3];
shell = FALSE;
}
else if (!strcmp (word[2], "-o"))
{
if (!*word[3])
return FALSE;
cmd = word_eol[3];
tochannel = TRUE;
}
if (shell)
{
if (access ("/bin/sh", X_OK) != 0)
{
fe_message (_("I need /bin/sh to run!\n"), FE_MSG_ERROR);
return TRUE;
}
}
#ifdef __EMX__ /* if os/2 */
if (pipe (fds) < 0)
{
PrintText (sess, "Pipe create error\n");
return FALSE;
}
setmode (fds[0], O_BINARY);
setmode (fds[1], O_BINARY);
#else
if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1)
{
PrintText (sess, "socketpair(2) failed\n");
return FALSE;
}
#endif
s = g_new0 (struct nbexec, 1);
s->myfd = fds[0];
s->tochannel = tochannel;
s->sess = sess;
pid = fork ();
if (pid == 0)
{
/* This is the child's context */
close (0);
close (1);
close (2);
/* Close parent's end of pipe */
close(s->myfd);
/* Copy the child end of the pipe to stdout and stderr */
dup2 (fds[1], 1);
dup2 (fds[1], 2);
/* Also copy it to stdin so we can write to it */
dup2 (fds[1], 0);
/* Now close all open file descriptors except stdin, stdout and stderr */
for (fd = 3; fd < 1024; fd++) close(fd);
/* Now we call /bin/sh to run our cmd ; made it more friendly -DC1 */
if (shell)
{
execl ("/bin/sh", "sh", "-c", cmd, NULL);
} else
{
char **argv;
int argc;
g_shell_parse_argv (cmd, &argc, &argv, NULL);
execvp (argv[0], argv);
g_strfreev (argv);
}
/* not reached unless error */
/*printf("exec error\n");*/
fflush (stdout);
_exit (0);
}
if (pid == -1)
{
/* Parent context, fork() failed */
PrintText (sess, "Error in fork(2)\n");
close(fds[0]);
close(fds[1]);
g_free (s);
}
else
{
/* Parent path */
close(fds[1]);
s->childpid = pid;
s->iotag = fe_input_add (s->myfd, FIA_READ|FIA_EX, exec_data, s);
sess->running_exec = s;
return TRUE;
}
}
return FALSE;
}
#endif
#if 0
/* export config stub */
static int
cmd_exportconf (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
/* this is pretty much the same as in hexchat_exit() */
save_config ();
if (prefs.save_pevents)
{
pevent_save (NULL);
}
sound_save ();
notify_save ();
ignore_save ();
free_sessions ();
chanopt_save_all ();
return TRUE; /* success */
return FALSE; /* fail */
}
#endif
static int
cmd_flushq (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
sprintf (tbuf, "Flushing server send queue, %d bytes.\n", sess->server->sendq_len);
PrintText (sess, tbuf);
sess->server->flush_queue (sess->server);
return TRUE;
}
static int
cmd_quit (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (*word_eol[2])
sess->quitreason = word_eol[2];
sess->server->disconnect (sess, TRUE, -1);
sess->quitreason = NULL;
return 2;
}
static int
cmd_gate (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *server_name = word[2];
server *serv = sess->server;
if (*server_name)
{
char *port = word[3];
#ifdef USE_OPENSSL
serv->use_ssl = FALSE;
#endif
server_fill_her_up (serv);
if (*port)
serv->connect (serv, server_name, atoi (port), TRUE);
else
serv->connect (serv, server_name, 23, TRUE);
return TRUE;
}
return FALSE;
}
typedef struct
{
char *cmd;
session *sess;
} getvalinfo;
static void
get_bool_cb (int val, getvalinfo *info)
{
char buf[512];
g_snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
if (is_session (info->sess))
handle_command (info->sess, buf, FALSE);
g_free (info->cmd);
g_free (info);
}
static int
cmd_getbool (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
getvalinfo *info;
if (!word[4][0])
return FALSE;
info = g_new (getvalinfo, 1);
info->cmd = g_strdup (word[2]);
info->sess = sess;
fe_get_bool (word[3], word_eol[4], get_bool_cb, info);
return TRUE;
}
static void
get_int_cb (int cancel, int val, getvalinfo *info)
{
char buf[512];
if (!cancel)
{
g_snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
if (is_session (info->sess))
handle_command (info->sess, buf, FALSE);
}
g_free (info->cmd);
g_free (info);
}
static int
cmd_getint (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
getvalinfo *info;
if (!word[4][0])
return FALSE;
info = g_new (getvalinfo, 1);
info->cmd = g_strdup (word[3]);
info->sess = sess;
fe_get_int (word[4], atoi (word[2]), get_int_cb, info);
return TRUE;
}
static void
get_file_cb (char *cmd, char *file)
{
char buf[1024 + 128];
/* execute the command once per file, then once more with
no args */
if (file)
{
g_snprintf (buf, sizeof (buf), "%s %s", cmd, file);
handle_command (current_sess, buf, FALSE);
}
else
{
handle_command (current_sess, cmd, FALSE);
g_free (cmd);
}
}
static int
cmd_getfile (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int idx = 2;
int flags = 0;
if (!word[3][0])
return FALSE;
if (!strcmp (word[2], "-folder"))
{
flags |= FRF_CHOOSEFOLDER;
idx++;
}
if (!strcmp (word[idx], "-multi"))
{
flags |= FRF_MULTIPLE;
idx++;
}
if (!strcmp (word[idx], "-save"))
{
flags |= FRF_WRITE;
idx++;
}
fe_get_file (word[idx+1], word[idx+2], (void *)get_file_cb, g_strdup (word[idx]), flags);
return TRUE;
}
static void
get_str_cb (int cancel, char *val, getvalinfo *info)
{
char buf[512];
if (!cancel)
{
g_snprintf (buf, sizeof (buf), "%s %s", info->cmd, val);
if (is_session (info->sess))
handle_command (info->sess, buf, FALSE);
}
g_free (info->cmd);
g_free (info);
}
static int
cmd_getstr (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
getvalinfo *info;
if (!word[4][0])
return FALSE;
info = g_new (getvalinfo, 1);
info->cmd = g_strdup (word[3]);
info->sess = sess;
fe_get_str (word[4], word[2], get_str_cb, info);
return TRUE;
}
static int
cmd_ghost (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (!word[2][0])
return FALSE;
sess->server->p_ns_ghost (sess->server, word[2], word[3]);
return TRUE;
}
static int
cmd_gui (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
switch (str_ihash (word[2]))
{
case 0x058b836e: fe_ctrl_gui (sess, 8, 0); break; /* APPLY */
case 0xac1eee45: fe_ctrl_gui (sess, 7, 2); break; /* ATTACH */
case 0x05a72f63: fe_ctrl_gui (sess, 4, atoi (word[3])); break; /* COLOR */
case 0xb06a1793: fe_ctrl_gui (sess, 7, 1); break; /* DETACH */
case 0x05cfeff0: fe_ctrl_gui (sess, 3, 0); break; /* FLASH */
case 0x05d154d8: fe_ctrl_gui (sess, 2, 0); break; /* FOCUS */
case 0x0030dd42: fe_ctrl_gui (sess, 0, 0); break; /* HIDE */
case 0x61addbe3: fe_ctrl_gui (sess, 5, 0); break; /* ICONIFY */
case 0xc0851aaa: fe_message (word[3], FE_MSG_INFO|FE_MSG_MARKUP); break; /* MSGBOX */
case 0x0035dafd: fe_ctrl_gui (sess, 1, 0); break; /* SHOW */
case 0x0033155f: /* MENU */
if (!g_ascii_strcasecmp (word[3], "TOGGLE"))
fe_ctrl_gui (sess, 6, 0);
else
return FALSE;
break;
default:
return FALSE;
}
return TRUE;
}
typedef struct
{
int longfmt;
int i, t;
char *buf;
} help_list;
static void
show_help_line (session *sess, help_list *hl, char *name, char *usage)
{
int j, len, max;
char *p;
if (name[0] == '.') /* hidden command? */
return;
if (hl->longfmt) /* long format for /HELP -l */
{
if (!usage || usage[0] == 0)
PrintTextf (sess, " \0034%s\003 :\n", name);
else
PrintTextf (sess, " \0034%s\003 : %s\n", name, _(usage));
return;
}
/* append the name into buffer, but convert to uppercase */
len = strlen (hl->buf);
p = name;
while (*p)
{
hl->buf[len] = toupper ((unsigned char) *p);
len++;
p++;
}
hl->buf[len] = 0;
hl->t++;
if (hl->t == 5)
{
hl->t = 0;
strcat (hl->buf, "\n");
PrintText (sess, hl->buf);
hl->buf[0] = ' ';
hl->buf[1] = ' ';
hl->buf[2] = 0;
} else
{
/* append some spaces after the command name */
max = strlen (name);
if (max < 10)
{
max = 10 - max;
for (j = 0; j < max; j++)
{
hl->buf[len] = ' ';
len++;
hl->buf[len] = 0;
}
}
}
}
static int
cmd_help (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int i = 0, longfmt = 0;
char *helpcmd = "";
GSList *list;
if (tbuf)
helpcmd = word[2];
if (*helpcmd && strcmp (helpcmd, "-l") == 0)
longfmt = 1;
if (*helpcmd && !longfmt)
{
help (sess, tbuf, helpcmd, FALSE);
} else
{
struct popup *pop;
char *buf = g_malloc (4096);
help_list hl;
hl.longfmt = longfmt;
hl.buf = buf;
PrintTextf (sess, "\n%s\n\n", _("Commands Available:"));
buf[0] = ' ';
buf[1] = ' ';
buf[2] = 0;
hl.t = 0;
hl.i = 0;
while (xc_cmds[i].name)
{
show_help_line (sess, &hl, xc_cmds[i].name, xc_cmds[i].help);
i++;
}
strcat (buf, "\n");
PrintText (sess, buf);
PrintTextf (sess, "\n%s\n\n", _("User defined commands:"));
buf[0] = ' ';
buf[1] = ' ';
buf[2] = 0;
hl.t = 0;
hl.i = 0;
list = command_list;
while (list)
{
pop = list->data;
show_help_line (sess, &hl, pop->name, pop->cmd);
list = list->next;
}
strcat (buf, "\n");
PrintText (sess, buf);
PrintTextf (sess, "\n%s\n\n", _("Plugin defined commands:"));
buf[0] = ' ';
buf[1] = ' ';
buf[2] = 0;
hl.t = 0;
hl.i = 0;
plugin_command_foreach (sess, &hl, (void *)show_help_line);
strcat (buf, "\n");
PrintText (sess, buf);
g_free (buf);
PrintTextf (sess, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
}
return TRUE;
}
static int
cmd_id (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (word[2][0])
{
sess->server->p_ns_identify (sess->server, word[2]);
return TRUE;
}
return FALSE;
}
static int
cmd_ignore (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int i;
int type = 0;
int quiet = 0;
char *mask;
if (!*word[2])
{
ignore_showlist (sess);
return TRUE;
}
if (!*word[3])
word[3] = "ALL";
i = 3;
while (1)
{
if (!*word[i])
{
if (type == 0)
return FALSE;
mask = word[2];
if (strchr (mask, '?') == NULL &&
strchr (mask, '*') == NULL)
{
mask = tbuf;
g_snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
}
i = ignore_add (mask, type, TRUE);
if (quiet)
return TRUE;
switch (i)
{
case 1:
EMIT_SIGNAL (XP_TE_IGNOREADD, sess, mask, NULL, NULL, NULL, 0);
break;
case 2: /* old ignore changed */
EMIT_SIGNAL (XP_TE_IGNORECHANGE, sess, mask, NULL, NULL, NULL, 0);
}
return TRUE;
}
if (!g_ascii_strcasecmp (word[i], "UNIGNORE"))
type |= IG_UNIG;
else if (!g_ascii_strcasecmp (word[i], "ALL"))
type |= IG_PRIV | IG_NOTI | IG_CHAN | IG_CTCP | IG_INVI | IG_DCC;
else if (!g_ascii_strcasecmp (word[i], "PRIV"))
type |= IG_PRIV;
else if (!g_ascii_strcasecmp (word[i], "NOTI"))
type |= IG_NOTI;
else if (!g_ascii_strcasecmp (word[i], "CHAN"))
type |= IG_CHAN;
else if (!g_ascii_strcasecmp (word[i], "CTCP"))
type |= IG_CTCP;
else if (!g_ascii_strcasecmp (word[i], "INVI"))
type |= IG_INVI;
else if (!g_ascii_strcasecmp (word[i], "QUIET"))
quiet = 1;
else if (!g_ascii_strcasecmp (word[i], "NOSAVE"))
type |= IG_NOSAVE;
else if (!g_ascii_strcasecmp (word[i], "DCC"))
type |= IG_DCC;
else
{
sprintf (tbuf, _("Unknown arg '%s' ignored."), word[i]);
PrintText (sess, tbuf);
}
i++;
}
}
static int
cmd_invite (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
if (!*word[2])
return FALSE;
if (*word[3])
sess->server->p_invite (sess->server, word[3], word[2]);
else
sess->server->p_invite (sess->server, sess->channel, word[2]);
return TRUE;
}
static int
cmd_join (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *chan = word[2];
session *sess_find;
if (*chan)
{
char *po, *pass = word[3];
sess_find = find_channel (sess->server, chan);
if (!sess_find)
{
sess->server->p_join (sess->server, chan, pass);
if (sess->channel[0] == 0 && sess->waitchannel[0])
{
po = strchr (chan, ',');
if (po)
*po = 0;
safe_strcpy (sess->waitchannel, chan, CHANLEN);
}
}
else
fe_ctrl_gui (sess_find, 2, 0); /* bring-to-front */
return TRUE;
}
return FALSE;
}
static int
cmd_kick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *nick = word[2];
char *reason = word_eol[3];
if (*nick)
{
sess->server->p_kick (sess->server, sess->channel, nick, reason);
return TRUE;
}
return FALSE;
}
static int
cmd_kickban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *nick = word[2];
char *reason = word_eol[3];
struct User *user;
if (*nick)
{
/* if the reason is a 1 digit number, treat it as a bantype */
user = userlist_find (sess, nick);
if (isdigit ((unsigned char) reason[0]) && reason[1] == 0)
{
ban (sess, tbuf, nick, reason, (user && user->op));
reason[0] = 0;
} else
ban (sess, tbuf, nick, "", (user && user->op));
sess->server->p_kick (sess->server, sess->channel, nick, reason);
return TRUE;
}
return FALSE;
}
static int
cmd_killall (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
hexchat_exit();
return 2;
}
static int
cmd_lagcheck (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
lag_check ();
return TRUE;
}
static void
lastlog (session *sess, char *search, gtk_xtext_search_flags flags)
{
session *lastlog_sess;
if (!is_session (sess))
return;
lastlog_sess = find_dialog (sess->server, "(lastlog)");
if (!lastlog_sess)
lastlog_sess = new_ircwindow (sess->server, "(lastlog)", SESS_DIALOG, 0);
lastlog_sess->lastlog_sess = sess;
lastlog_sess->lastlog_flags = flags;
fe_text_clear (lastlog_sess, 0);
fe_lastlog (sess, lastlog_sess, search, flags);
}
static int
cmd_lastlog (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
int j = 2;
gtk_xtext_search_flags flags = 0;
gboolean doublehyphen = FALSE;
while (word_eol[j] != NULL && word_eol [j][0] == '-' && !doublehyphen)
{
switch (word_eol [j][1])
{
case 'r':
flags |= regexp;
break;
case 'm':
flags |= case_match;
break;
case 'h':
flags |= highlight;
break;
case '-':
doublehyphen = TRUE;
break;
default:
break;
/* O dear whatever shall we do here? */
}
j++;
}
if (word_eol[j] != NULL && *word_eol[j])
{
lastlog (sess, word_eol[j], flags);
return TRUE;
}
else
{
return FALSE;
}
}
static int
cmd_list (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
fe_open_chan_list (sess->server, word_eol[2], TRUE);
return TRUE;
}
gboolean
load_perform_file (session *sess, char *file)
{
char tbuf[1024 + 4];
char *nl;
FILE *fp;
fp = hexchat_fopen_file (file, "r", 0); /* load files from config dir */
if (!fp)
return FALSE;
tbuf[1024] = 0;
while (fgets (tbuf, 1024, fp))
{
nl = strchr (tbuf, '\n');
if (nl == tbuf) /* skip empty commands */
continue;
if (nl)
*nl = 0;
if (tbuf[0] == prefs.hex_input_command_char[0])
handle_command (sess, tbuf + 1, TRUE);
else
handle_command (sess, tbuf, TRUE);
}
fclose (fp);
return TRUE;
}
static int
cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *file, *buf;
#ifdef USE_PLUGIN
char *error, *arg;
#endif
if (!word[2][0])
return FALSE;
if (strcmp (word[2], "-e") == 0)
{
file = expand_homedir (word[3]);
if (!load_perform_file (sess, file))
{
buf = g_strdup_printf ("%s%c%s", get_xdir(), G_DIR_SEPARATOR, file);
PrintTextf (sess, _("Cannot access %s\n"), buf);
PrintText (sess, errorstring (errno));
g_free (buf);
}
g_free (file);
return TRUE;
}
#ifdef USE_PLUGIN
if (g_str_has_suffix (word[2], "."G_MODULE_SUFFIX))
{
arg = NULL;
if (word_eol[3][0])
arg = word_eol[3];
file = expand_homedir (word[2]);
error = plugin_load (sess, file, arg);
g_free (file);
if (error)
PrintText (sess, error);
return TRUE;
}
sprintf (tbuf, "Unknown file type %s. Maybe you need to install the Perl or Python plugin?\n", word[2]);
PrintText (sess, tbuf);
#endif
return FALSE;
}
char *
split_up_text(struct session *sess, char *text, int cmd_length, char *split_text)
{
unsigned int max, space_offset;
char *space;
/* maximum allowed text */
/* :nickname!username@host.com cmd_length */
max = 512; /* rfc 2812 */
max -= 3; /* :, !, @ */
max -= cmd_length;
max -= strlen (sess->server->nick);
max -= strlen (sess->channel);
if (sess->me && sess->me->hostname)
max -= strlen (sess->me->hostname);
else
{
max -= 9; /* username */
max -= 65; /* max possible hostname and '@' */
}
if (strlen (text) > max)
{
unsigned int i = 0;
int size;
/* traverse the utf8 string and find the nearest cut point that
doesn't split 1 char in half */
while (1)
{
size = g_utf8_skip[((unsigned char *)text)[i]];
if ((i + size) >= max)
break;
i += size;
}
max = i;
/* Try splitting at last space */
space = g_utf8_strrchr (text, max, ' ');
if (space)
{
space_offset = g_utf8_pointer_to_offset (text, space);
/* Only split if last word is of sane length */
if (max != space_offset && max - space_offset < 20)
max = space_offset + 1;
}
split_text = g_strdup_printf ("%.*s", max, text);
return split_text;
}
return NULL;
}
static int
cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char *act = word_eol[2];
char *split_text = NULL;
int cmd_length = 22; /* " PRIVMSG ", " ", :, \001ACTION, " ", \001, \r, \n */
int offset = 0;
message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
if (!(*act))
return FALSE;
if (sess->type == SESS_SERVER)
{
notj_msg (sess);
return TRUE;
}
g_snprintf (tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
/* first try through DCC CHAT */
if (dcc_write_chat (sess->channel, tbuf))
{
/* print it to screen */
inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE,
&no_tags);
} else
{
/* DCC CHAT failed, try through server */
if (sess->server->connected)
{
while ((split_text = split_up_text (sess, act + offset, cmd_length, split_text)))
{
sess->server->p_action (sess->server, sess->channel, split_text);
/* print it to screen */
inbound_action (sess, sess->channel, sess->server->nick, "",
split_text, TRUE, FALSE,
&no_tags);
if (*split_text)
offset += strlen(split_text);
g_free (split_text);
}
sess->server->p_action (sess->server, sess->channel, act + offset);
/* print it to screen */
inbound_action (sess, sess->channel, sess->server->nick, "",
act + offset, TRUE, FALSE, &no_tags);
} else
{
notc_msg (sess);
}
}
return TRUE;
}
static int
cmd_mode (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
/* We allow omitting the target, so we have to figure it out:
* - Can only use info from channels or dialogs
* - Empty arg is always sess info
* - Assume + is mode not channel
* - We know valid channels and our nick
* - We cannot easily know if other nick or valid mode (Need to store 004)
*/
if ((sess->type != SESS_CHANNEL && sess->type != SESS_DIALOG)
|| (!(*word[2] == '-' || *word[2] == '+' || *word[2] == '\0')
&& (is_channel (sess->server, word[2]) || !rfc_casecmp (sess->server->nick, word[2])))
)
{
sess->server->p_mode (sess->server, word[2], word_eol[3]);
}
else
{
if(sess->channel[0] == 0)
return FALSE;
sess->server->p_mode (sess->server, sess->channel, word_eol[2]);
}
return TRUE;
}
static int
mop_cb (struct User *user, multidata *data)
{
if (!user->op)
{
data->nicks[data->i] = user->nick;
data->i++;
}
return TRUE;
}
static int
cmd_mop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
{
char **nicks = g_new0 (char *, sess->total - sess->ops);
multidata data;
data.nicks = nicks;
data.i = 0;
tree_foreach (sess->usertree, (tree_traverse_func *)mop_cb, &data);
send_channel_modes (sess, tbuf, nicks, 0, data.i, '+', 'o', 0);