Files
bitchx/source/parse.c
Kevin Easton 06aa5cb671 Move all handling of SED messages and notices into ctcp.c, re-enable SED notices
Actual encryped messages and notices are now printed directly from do_sed() / do_reply_sed().
Inline CTCP replacement is only done if the message cannot be decrypted (for the [ENCRYPTED MESSAGE]
placeholder).

This removes the need for the global flag 'sed' to alter the NOTICE and PRIVMSG handling.

A side-effect of this is that SED PRIVMSGs now do not go through the usual PRIVMSG ignore
and flood handling.  This is acceptable because messages can only go through this path if
the sender has actually been added as a SED peer with /ENCRYPT, and it still goes through
the CTCP ignore and flood handling.
2017-06-26 15:13:40 +10:00

2011 lines
54 KiB
C

/*
* parse.c: handles messages from the server. Believe it or not. I
* certainly wouldn't if I were you.
*
* Written By Michael Sandrof
*
* Copyright(c) 1990
* Modified Colten Edwards 1997
*/
#include "irc.h"
static char cvsrevision[] = "$Id$";
CVS_REVISION(parse_c)
#include "struct.h"
#include "alias.h"
#include "server.h"
#include "names.h"
#include "vars.h"
#include "cdcc.h"
#include "ctcp.h"
#include "hook.h"
#include "log.h"
#include "commands.h"
#include "ignore.h"
#include "who.h"
#include "lastlog.h"
#include "input.h"
#include "ircaux.h"
#include "funny.h"
#include "encrypt.h"
#include "input.h"
#include "ircterm.h"
#include "flood.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#include "numbers.h"
#include "parse.h"
#include "notify.h"
#include "status.h"
#include "list.h"
#include "userlist.h"
#include "misc.h"
#include "whowas.h"
#include "timer.h"
#include "keys.h"
#include "hash2.h"
#include "cset.h"
#include "module.h"
#include "hash2.h"
#include "gui.h"
#include "tcl_bx.h"
#define MAIN_SOURCE
#include "modval.h"
#define STRING_CHANNEL '+'
#define MULTI_CHANNEL '#'
#define LOCAL_CHANNEL '&'
#define ID_CHANNEL '!'
static void strip_modes (char *, char *, char *);
char *last_split_server = NULL;
char *last_split_from = NULL;
/*
* joined_nick: the nickname of the last person who joined the current
* channel
*/
char *joined_nick = NULL;
/* public_nick: nick of the last person to send a message to your channel */
char *public_nick = NULL;
/* User and host information from server 2.7 */
char *FromUserHost = empty_string;
/* doing a PRIVMSG */
int doing_privmsg = 0;
#define do_output(x) put_it("%s%s%s", get_int_var(TIMESTAMP_VAR) ? update_clock(GET_TIME):empty_string, get_int_var(TIMESTAMP_VAR) ? space: empty_string, x)
/* returns ban if the ban is on the channel already, NULL if not */
BanList *ban_is_on_channel(register char *ban, register ChannelList *chan)
{
register BanList *bans = NULL;
#if 0
register BanList *eban = NULL;
#endif
for (bans = chan->bans; bans; bans = bans->next)
{
if (wild_match(bans->ban, ban) || wild_match(ban, bans->ban))
break;
else if (!my_stricmp(bans->ban, ban))
break;
}
return bans;
#if 0
for (eban = chan->exemptbans; eban; eban = eban->next)
{
if (wild_match(eban->ban, ban) || wild_match(ban, eban->ban))
break;
else if (!my_stricmp(eban->ban, ban))
break;
}
return eban ? NULL : bans;
#endif
}
BanList *eban_is_on_channel(register char *ban, register ChannelList *chan)
{
register BanList *eban = NULL;
for (eban = chan->exemptbans; eban; eban = eban->next)
{
if (wild_match(eban->ban, ban) || wild_match(ban, eban->ban))
break;
else if (!my_stricmp(eban->ban, ban))
break;
}
return eban ? eban : NULL;
}
void fake (void)
{
bitchsay("--- Fake Message received!!! ---");
return;
}
int check_auto_reply(char *str)
{
char *p = NULL;
char *pat;
if (!str || !*str || !get_int_var(AUTO_RESPONSE_VAR) || !auto_str)
return 0;
p = LOCAL_COPY(auto_str);
while ((pat = next_arg(p, &p)))
{
switch(get_int_var(NICK_COMPLETION_TYPE_VAR))
{
case 3:
if (!my_stricmp(str, pat))
goto found_auto;
continue;
case 2:
if (wild_match(pat, str))
goto found_auto;
continue;
case 1:
if (stristr(str, pat))
goto found_auto;
continue;
default:
case 0:
if (!my_strnicmp(str, pat, strlen(pat)))
goto found_auto;
continue;
}
}
return 0;
found_auto:
#ifdef GUI
gui_activity(COLOR_HIGHLIGHT);
#endif
return 1;
}
/* Check for more than 75% ALLCAPS */
static int annoy_caps(const char *crap)
{
int total = 0, allcaps = 0;
/* removed from ComStud client */
while (*crap)
{
if (isalpha((unsigned char)*crap))
{
total++;
if (isupper((unsigned char)*crap))
allcaps++;
}
crap++;
}
return total > 12 && (double)allcaps / total >= 0.75;
}
/* Check for more than 75% of printable chars highlighted */
static int annoy_hl(const char *crap, char hl_tog)
{
int total = 0, all_hl = 0, in_highlight = 0;
while (*crap)
{
if (*crap == hl_tog)
in_highlight = !in_highlight;
if (isgraph((unsigned char)*crap))
{
total++;
if (in_highlight)
all_hl++;
}
crap++;
}
return total > 12 && (double)all_hl / total >= 0.75;
}
int annoy_kicks(int list_type, char *to, char *from, char *ptr, NickList *nick)
{
int kick_em = 0;
ChannelList *chan;
if (nick && (nick->userlist && nick->userlist->flags))
return 0;
if (!check_channel_match(get_string_var(PROTECT_CHANNELS_VAR), to) || !are_you_opped(to))
return 0;
if (!(chan = lookup_channel(to, from_server, CHAN_NOUNLINK)))
return 0;
if (get_cset_int_var(chan->csets, ANNOY_KICK_CSET) && !nick_isop(nick))
{
char *buffer = NULL;
if (annoy_hl(ptr, BOLD_TOG))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for " BOLD_TOG_STR "bold" BOLD_TOG_STR);
else if (strchr(ptr, BELL_CHAR))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for beeping");
else if (annoy_hl(ptr, COLOR_CHAR))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for " UND_TOG_STR "mirc color" UND_TOG_STR);
else if (annoy_hl(ptr, UND_TOG))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for " UND_TOG_STR "underline" UND_TOG_STR);
else if (annoy_hl(ptr, REV_TOG))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for " REV_TOG_STR "inverse" REV_TOG_STR);
else if (annoy_hl(ptr, BLINK_TOG))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for " BLINK_TOG_STR "flashing" BLINK_TOG_STR);
else if (annoy_caps(ptr))
malloc_sprintf(&buffer, "KICK %s %s :%s", to, from, "autokick for CAPS LOCK");
else if (strstr(ptr, "0000027fed"))
{
char *host = NULL, *p;
malloc_strcpy(&host, FromUserHost);
p = strchr(host, '@'); *p++ = '\0';
send_to_server("MODE %s -o+b %s *!*%s", to, from, cluster(FromUserHost));
send_to_server("KICK %s %s :%s", to, from, "\002Zmodem rocks\002");
if (get_int_var(AUTO_UNBAN_VAR))
add_timer(0, empty_string, get_int_var(AUTO_UNBAN_VAR) * 1000, 1, timer_unban, m_sprintf("%d %s *!*%s", from_server, to, cluster(FromUserHost)), NULL, current_window->refnum, "auto-unban");
new_free(&host);
kick_em = 1;
}
if (buffer)
{
kick_em = 1;
send_to_server("%s", buffer);
new_free(&buffer);
}
}
if (ban_words)
{
WordKickList *word;
int ops = get_cset_int_var(chan->csets, KICK_OPS_CSET);
for (word = ban_words; word; word = word->next)
{
if (stristr(ptr, word->string))
{
if (wild_match(word->channel, to))
{
if (!ops && (nick_isop(nick) || nick_isvoice(nick)))
break;
send_to_server("KICK %s %s :%s %s", to, from, "\002BitchX BWK\002: ", word->string);
kick_em = 1;
break;
}
}
}
}
return kick_em;
}
/*
* is_channel: determines if the argument is a channel. If it's a number,
* begins with MULTI_CHANNEL and has no '*', or STRING_CHANNEL, then its a
* channel
*/
int BX_is_channel(char *to)
{
if (!to || !*to)
return 0;
return ( (to) && ((*to == MULTI_CHANNEL)
|| (*to == STRING_CHANNEL)
|| (*to == ID_CHANNEL)
|| (*to == LOCAL_CHANNEL)));
}
char * BX_PasteArgs(char **Args, int StartPoint)
{
int i;
for (; StartPoint; Args++, StartPoint--)
if (!*Args)
return NULL;
for (i = 0; Args[i] && Args[i+1]; i++)
Args[i][strlen(Args[i])] = ' ';
Args[1] = NULL;
return Args[0];
}
/*
* BreakArgs: breaks up the line from the server, in to where its from,
* setting FromUserHost if it should be, and returns all the arguements
* that are there. Re-written by phone, dec 1992.
*/
int BX_BreakArgs(char *Input, char **Sender, char **OutPut, int ig_sender)
{
int ArgCount = 0;
/*
* The RFC describes it fully, but in a short form, a line looks like:
* [:sender[!user@host]] COMMAND ARGUMENT [[:]ARGUMENT]{0..14}
*/
/*
* Look to see if the optional :sender is present.
*/
if (!ig_sender)
{
if (*Input == ':')
{
*Sender = ++Input;
while (*Input && *Input != *space)
Input++;
if (*Input == *space)
*Input++ = 0;
/*
* Look to see if the optional !user@host is present.
* look for @host only as services use it.
*/
FromUserHost = *Sender;
while (*FromUserHost && *FromUserHost != '!' && *FromUserHost != '@')
FromUserHost++;
if (*FromUserHost == '!' || *FromUserHost == '@')
*FromUserHost++ = 0;
}
/*
* No sender present.
*/
else
*Sender = FromUserHost = empty_string;
}
/*
* Now we go through the argument list...
*/
for (;;)
{
while (*Input && *Input == *space)
Input++;
if (!*Input)
break;
if (*Input == ':')
{
OutPut[ArgCount++] = ++Input;
break;
}
OutPut[ArgCount++] = Input;
if (ArgCount >= MAXPARA)
break;
while (*Input && *Input != *space)
Input++;
if (*Input == *space)
*Input++ = 0;
}
OutPut[ArgCount] = NULL;
return ArgCount;
}
/* in response to a TOPIC message from the server */
static void p_topic(char *from, char **ArgList)
{
ChannelList *tmp;
if (!ArgList[1])
{ fake(); return; }
if ((tmp = lookup_channel(ArgList[0], from_server, CHAN_NOUNLINK)))
{
update_stats(TOPICLIST, find_nicklist_in_channellist(from, tmp, 0), tmp, 0);
if (tmp->topic_lock)
{
if (my_stricmp(from, get_server_nickname(from_server)))
{
if (tmp->topic)
{
if (!ArgList[1] || my_stricmp(ArgList[1], tmp->topic))
send_to_server("TOPIC %s :%s", tmp->channel, tmp->topic);
}
} else
malloc_strcpy(&tmp->topic, ArgList[1]);
} else
malloc_strcpy(&tmp->topic, ArgList[1]);
add_last_type(&last_topic[0], 1, from, FromUserHost, tmp->channel, ArgList[1]);
do_logchannel(LOG_TOPIC, tmp, "%s %s %s", from, ArgList[0], ArgList[1] ? ArgList[1] : empty_string);
}
if (tmp && check_ignore(from, FromUserHost, tmp->channel, IGNORE_TOPICS, NULL) != IGNORED)
{
set_display_target(ArgList[0], LOG_CRAP);
if (do_hook(TOPIC_LIST, "%s %s %s", from, ArgList[0], ArgList[1]))
{
if (ArgList[1] && *ArgList[1])
{
if (fget_string_var(FORMAT_TOPIC_CHANGE_HEADER_FSET))
put_it("%s",convert_output_format(fget_string_var(FORMAT_TOPIC_CHANGE_HEADER_FSET), "%s %s %s %s", update_clock(GET_TIME), from, ArgList[0], ArgList[1]));
put_it("%s",convert_output_format(fget_string_var(FORMAT_TOPIC_CHANGE_FSET), "%s %s %s %s", update_clock(GET_TIME), from, ArgList[0], ArgList[1]));
} else
put_it("%s",convert_output_format(fget_string_var(FORMAT_TOPIC_UNSET_FSET), "%s %s %s", update_clock(GET_TIME), from, ArgList[0]));
}
reset_display_target();
}
logmsg(LOG_TOPIC, from, 0, "%s %s", ArgList[0], ArgList[1] ? ArgList[1]:empty_string);
#ifdef GUI
xterm_settitle();
#endif
update_all_status(current_window, NULL, 0);
}
static void p_wallops(char *from, char **ArgList)
{
char *line;
int autorep = 0;
int from_server = strchr(from, '.') ? 1 : 0;
if (!(line = PasteArgs(ArgList, 0)))
{ fake(); return; }
if (from_server || check_flooding(from, WALLOP_FLOOD,line, NULL))
{
/* The old server check, don't use the whois stuff for servers */
int level;
char *high;
switch (check_ignore(from, FromUserHost, NULL, IGNORE_WALLOPS, NULL))
{
case (IGNORED):
return;
case (HIGHLIGHTED):
high = highlight_char;
break;
default:
high = empty_string;
break;
}
set_display_target(from, LOG_WALLOP);
level = set_lastlog_msg_level(LOG_WALLOP);
autorep = check_auto_reply(line);
add_last_type(&last_wall[0], MAX_LAST_MSG, from, NULL, from_server ? "S":"*", line);
if (do_hook(WALLOP_LIST, "%s %c %s", from, from_server ? 'S': '*',line))
put_it("%s",convert_output_format(fget_string_var(from_server? FORMAT_WALLOP_FSET: (autorep? FORMAT_WALL_AR_FSET:FORMAT_WALL_FSET)),
"%s %s %s %s", update_clock(GET_TIME),
from, from_server?"!":"*", line));
if (beep_on_level & LOG_WALLOP)
beep_em(1);
logmsg(LOG_WALLOP, from, 0, "%s", line);
set_lastlog_msg_level(level);
reset_display_target();
}
}
static void p_privmsg(char *from, char **Args)
{
int level,
list_type,
flood_type,
log_type,
ar_true = 0,
no_flood = 1,
do_beep = 0;
unsigned char ignore_type;
char *ptr = NULL,
*to,
*high;
ChannelList *channel = NULL;
NickList *tmpnick = NULL;
if (!from)
return;
PasteArgs(Args, 1);
to = Args[0];
ptr = Args[1];
if (!to || !ptr)
{ fake(); return; }
doing_privmsg = 1;
ptr = do_ctcp(from, to, ptr);
if (!ptr || !*ptr)
{
doing_privmsg = 0;
return;
}
if (is_channel(to) && im_on_channel(to, from_server))
{
set_display_target(to, LOG_PUBLIC);
malloc_strcpy(&public_nick, from);
log_type = LOG_PUBLIC;
ignore_type = IGNORE_PUBLIC;
flood_type = PUBLIC_FLOOD;
if (!is_on_channel(to, from_server, from))
list_type = PUBLIC_MSG_LIST;
else
{
if (is_current_channel(to, from_server, 0))
list_type = PUBLIC_LIST;
else
list_type = PUBLIC_OTHER_LIST;
channel = lookup_channel(to, from_server, CHAN_NOUNLINK);
if (channel)
tmpnick = find_nicklist_in_channellist(from, channel, 0);
}
}
else
{
set_display_target(from, LOG_MSG);
flood_type = MSG_FLOOD;
if (my_stricmp(to, get_server_nickname(from_server)))
{
log_type = LOG_WALL;
ignore_type = IGNORE_WALLS;
list_type = MSG_GROUP_LIST;
flood_type = WALL_FLOOD;
}
else
{
log_type = LOG_MSG;
ignore_type = IGNORE_MSGS;
list_type = MSG_LIST;
}
}
switch (check_ignore(from, FromUserHost, to, ignore_type, ptr))
{
case IGNORED:
if ((list_type == MSG_LIST) && get_int_var(SEND_IGNORE_MSG_VAR))
send_to_server("NOTICE %s :%s is ignoring you", from, get_server_nickname(from_server));
doing_privmsg = 0;
return;
case HIGHLIGHTED:
high = highlight_char;
break;
case CHANNEL_GREP:
high = highlight_char;
break;
default:
high = empty_string;
break;
}
#ifdef WANT_TCL
{
int x = 0;
char *cmd = NULL;
switch(list_type)
{
case MSG_LIST:
case MSG_GROUP_LIST:
{
char *ctcp_ptr;
ctcp_ptr = LOCAL_COPY(ptr);
cmd = next_arg(ctcp_ptr, &ctcp_ptr);
x = check_tcl_msg(cmd, from, FromUserHost, from, ctcp_ptr);
if (!x)
check_tcl_msgm(cmd, from, FromUserHost, from, ctcp_ptr);
break;
}
case PUBLIC_MSG_LIST:
case PUBLIC_LIST:
case PUBLIC_OTHER_LIST:
{
x = check_tcl_pub(from, FromUserHost, to, ptr);
if (!x)
check_tcl_pubm(from, FromUserHost, to, ptr);
break;
}
}
}
#endif
update_stats(PUBLICLIST, tmpnick, channel, 0);
level = set_lastlog_msg_level(log_type);
if (flood_type == PUBLIC_FLOOD)
{
int blah = 0;
if (is_other_flood(channel, tmpnick, PUBLIC_FLOOD, &blah))
{
no_flood = 0;
flood_prot(tmpnick->nick, FromUserHost, flood_type, get_cset_int_var(channel->csets, PUBFLOOD_IGNORE_TIME_CSET), channel->channel);
}
}
else
no_flood = check_flooding(from, flood_type, ptr, NULL);
{
int added_to_tab = 0;
if (list_type == PUBLIC_LIST || list_type == PUBLIC_OTHER_LIST || list_type == PUBLIC_MSG_LIST)
{
if (check_auto_reply(ptr))
{
addtabkey(from, "msg", 1);
ar_true = 1;
added_to_tab = 1;
}
}
switch (list_type)
{
case PUBLIC_MSG_LIST:
{
if (!no_flood)
break;
if (do_hook(list_type, "%s %s %s", from, to, ptr))
{
logmsg(LOG_PUBLIC, from, 0, "%s %s", to, ptr);
put_it("%s",convert_output_format(fget_string_var(ar_true?FORMAT_PUBLIC_MSG_AR_FSET:FORMAT_PUBLIC_MSG_FSET), "%s %s %s %s %s", update_clock(GET_TIME), from, FromUserHost, to, ptr));
do_beep = 1;
}
break;
}
case MSG_GROUP_LIST:
{
if (!no_flood)
break;
if (do_hook(list_type, "%s %s %s", from, to, ptr))
{
logmsg(LOG_PUBLIC, from, 0,"%s %s", FromUserHost, ptr);
put_it("%s", convert_output_format(fget_string_var(FORMAT_MSG_GROUP_FSET), "%s %s %s %s", update_clock(GET_TIME), from, to, ptr));
do_beep = 1;
}
break;
}
case MSG_LIST:
{
if (!no_flood)
break;
set_server_recv_nick(from_server, from);
#ifdef WANT_CDCC
if ((msgcdcc(from, to, ptr)) == NULL)
break;
#endif
if (strbegins(ptr, "PASS") && change_pass(from, ptr))
break;
if (forwardnick)
send_to_server("NOTICE %s :*%s* %s", forwardnick, from, ptr);
if (do_hook(list_type, "%s %s", from, ptr))
{
if (get_server_away(from_server))
{
do_beep = 0;
beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));
set_int_var(MSGCOUNT_VAR, get_int_var(MSGCOUNT_VAR)+1);
}
else
do_beep = 1;
put_it("%s", convert_output_format(fget_string_var(FORMAT_MSG_FSET), "%s %s %s %s", update_clock(GET_TIME), from, FromUserHost, ptr));
if (!added_to_tab)
addtabkey(from, "msg", 0);
logmsg(LOG_MSG, from, 0,"%s %s", FromUserHost, ptr);
}
add_last_type(&last_msg[0], MAX_LAST_MSG, from, FromUserHost, to, ptr);
if (get_server_away(from_server) && get_int_var(SEND_AWAY_MSG_VAR))
{
if (!check_last_type(&last_msg[0], MAX_LAST_MSG, from, FromUserHost))
my_send_to_server(from_server, "NOTICE %s :%s", from, stripansicodes(convert_output_format(fget_string_var(FORMAT_SEND_AWAY_FSET), "%l %l %s", now, get_server_awaytime(from_server), get_int_var(MSGLOG_VAR)?"On":"Off")));
}
break;
}
case PUBLIC_LIST:
{
if (!no_flood)
break;
annoy_kicks(list_type, to, from, ptr, tmpnick);
if (ar_true)
list_type = PUBLIC_AR_LIST;
if (do_hook(list_type, "%s %s %s", from, to, ptr))
{
logmsg(LOG_PUBLIC, from, 0,"%s %s", to, ptr);
do_logchannel(LOG_PUBLIC, channel, "%s", convert_output_format(fget_string_var((list_type == PUBLIC_AR_LIST)? FORMAT_PUBLIC_AR_FSET:FORMAT_PUBLIC_FSET), "%s %s %s %s", update_clock(GET_TIME), from, to, ptr));
put_it("%s", convert_output_format(fget_string_var((list_type == PUBLIC_AR_LIST)? FORMAT_PUBLIC_AR_FSET:FORMAT_PUBLIC_FSET), "%s %s %s %s", update_clock(GET_TIME), from, to, ptr));
do_beep = 1;
}
break;
}
case PUBLIC_OTHER_LIST:
{
if (!no_flood)
break;
annoy_kicks(list_type, to, from, ptr, tmpnick);
if (ar_true)
list_type = PUBLIC_OTHER_AR_LIST;
if (do_hook(list_type, "%s %s %s", from, to, ptr))
{
logmsg(LOG_PUBLIC, from, 0,"%s %s", to, ptr);
do_logchannel(LOG_PUBLIC, channel, "%s", convert_output_format(fget_string_var(list_type==PUBLIC_OTHER_AR_LIST?FORMAT_PUBLIC_OTHER_AR_FSET:FORMAT_PUBLIC_OTHER_FSET), "%s %s %s %s", update_clock(GET_TIME), from, to, ptr));
put_it("%s", convert_output_format(fget_string_var(list_type==PUBLIC_OTHER_AR_LIST?FORMAT_PUBLIC_OTHER_AR_FSET:FORMAT_PUBLIC_OTHER_FSET), "%s %s %s %s", update_clock(GET_TIME), from, to, ptr));
do_beep = 1;
}
break;
} /* case */
} /* switch */
}
if ((beep_on_level & log_type) && do_beep)
beep_em(1);
if (no_flood)
grab_http(from, to, ptr);
set_lastlog_msg_level(level);
reset_display_target();
doing_privmsg = 0;
}
static void p_quit(char *from, char **ArgList)
{
int one_prints = 0;
char *reason;
char *chanlist = NULL;
ChannelList *chan;
int netsplit = 0;
int ignore;
PasteArgs(ArgList, 0);
if (ArgList[0])
{
reason = ArgList[0];
netsplit = check_split(from, reason);
}
else
reason = "?";
for (chan = walk_channels(from, 1, from_server); chan;
chan = walk_channels(from, 0, -1))
{
update_stats(CHANNELSIGNOFFLIST,
find_nicklist_in_channellist(from, chan, 0), chan, netsplit);
#ifdef WANT_TCL
if (netsplit)
check_tcl_split(from, FromUserHost, from, chan->channel);
else
check_tcl_sign(from, FromUserHost, from, chan->channel, reason);
#endif
if (!netsplit)
{
do_logchannel(LOG_PART, chan, "%s %s %s %s", from, FromUserHost,
chan->channel, reason);
check_channel_limit(chan);
}
if (chanlist)
m_3cat(&chanlist, ",", chan->channel);
else
malloc_strcpy(&chanlist, chan->channel);
ignore = check_ignore(from, FromUserHost, chan->channel,
(netsplit?IGNORE_SPLITS:IGNORE_QUITS), NULL);
if (ignore != IGNORED)
{
set_display_target(chan->channel, LOG_CRAP);
if (do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", chan->channel,
from, reason))
one_prints = 1;
}
}
if (one_prints)
{
char *channel = what_channel(from, from_server);
ignore = check_ignore(from, FromUserHost, channel,
(netsplit?IGNORE_SPLITS:IGNORE_QUITS), NULL);
set_display_target(channel, LOG_CRAP);
if ((ignore != IGNORED) && do_hook(SIGNOFF_LIST, "%s %s", from, reason)
&& !netsplit)
put_it("%s", convert_output_format(
fget_string_var(FORMAT_CHANNEL_SIGNOFF_FSET),
"%s %s %s %s %s", update_clock(GET_TIME), from, FromUserHost,
chanlist, reason));
}
logmsg(LOG_PART, from, 0, "%s %s", chanlist ? chanlist : "<NONE>", reason);
check_orig_nick(from);
notify_mark(from, FromUserHost, 0, 0);
remove_from_channel(NULL, from, from_server, netsplit, reason);
update_all_status(current_window, NULL, 0);
new_free(&chanlist);
reset_display_target();
#ifdef GUI
gui_update_nicklist(NULL);
#endif
}
static int sping_reply(char *from, char *sping_dest, int server)
{
char buff[50];
Sping *tmp = get_server_sping(server, sping_dest);
if (!tmp)
return 0;
snprintf(buff, sizeof buff, "%2.4f", time_since(&tmp->in_sping));
reset_display_target();
put_it("%s", convert_output_format("$G Server pong from %W$0%n $1 seconds", "%s %s", from, buff));
clear_server_sping(server, sping_dest);
return 1;
}
static void p_pong(char *from, char **ArgList)
{
int is_server = 0;
if (!ArgList[0] || !ArgList[1])
return;
is_server = wild_match("*.*", ArgList[0]);
if (check_ignore(from, FromUserHost, NULL, IGNORE_PONGS, NULL) == IGNORED)
return;
if (!is_server)
return;
if (strbegins(ArgList[1], "LAG!"))
{
/* PONG for lag check */
char *p, *q;
unsigned long cookie;
struct timeval timenow, timethen;
get_time(&timenow);
p = strchr(ArgList[1], '.');
if (p)
{
*p++ = 0;
cookie = strtoul(ArgList[1] + 4, NULL, 10);
q = strchr(p, '.');
if (q)
{
*q++ = 0;
timethen.tv_usec = my_atol(q);
} else
timethen.tv_usec = 0;
timethen.tv_sec = my_atol(p);
server_lag_reply(from_server, cookie, timenow, timethen);
}
}
else if (!my_stricmp(ArgList[1], get_server_nickname(from_server)))
{
/* PONG from remote server */
sping_reply(ArgList[0], ArgList[0], from_server);
}
else if (wild_match("*.*", ArgList[1]))
{
/* PONG from local server, possibly on behalf of remote server */
sping_reply(ArgList[0], ArgList[1], from_server);
}
else
{
reset_display_target();
say("%s: PONG received from %s %s", ArgList[0], from, ArgList[1]);
}
}
static void p_error(char *from, char **ArgList)
{
PasteArgs(ArgList, 0);
if (!ArgList[0])
{
fake();
return;
}
say("%s", ArgList[0]);
}
/*
* This only handles negotiating the SASL capability with the PLAIN method. It would
* be good to add DH-BLOWFISH, and later, full capability support.
*/
static void p_cap(char *from, char **ArgList)
{
char *caps, *p;
if (!strcmp(ArgList[1], "ACK"))
{
caps = LOCAL_COPY(ArgList[2]);
while ((p = next_arg(caps, &caps)) != NULL)
{
/* Only AUTHENTICATE before registration */
if (!strcmp(p, "sasl") && !is_server_connected(from_server))
{
my_send_to_server(from_server, "AUTHENTICATE PLAIN");
break;
}
}
}
else if (!strcmp(ArgList[1], "NAK"))
{
caps = LOCAL_COPY(ArgList[2]);
while ((p = next_arg(caps, &caps)) != NULL)
{
/* End capability negotiation to continue registration */
if (!strcmp(p, "sasl") && !is_server_connected(from_server))
{
my_send_to_server(from_server, "CAP END");
break;
}
}
}
}
static void p_authenticate(char *from, char **ArgList)
{
/* "AUTHENTICATE command MUST be used before registration is complete" */
if (is_server_connected(from_server))
return;
if (!strcmp(ArgList[0], "+"))
{
/* Message is BASE64(nick\0nick\0pass) */
char buf[IRCD_BUFFER_SIZE];
char *output = NULL;
char *nick = get_server_sasl_nick(from_server);
char *pass = get_server_sasl_pass(from_server);
size_t nick_len = nick ? strlen(nick) + 1 : 0; /* nick_len includes \0 */
size_t pass_len = pass ? strlen(pass) : 0;
/* "The client can abort an authentication by sending an asterisk as the data" */
if (!nick || !pass || nick_len * 2 + pass_len > sizeof buf)
{
my_send_to_server(from_server, "AUTHENTICATE *");
return;
}
memcpy(buf, nick, nick_len);
memcpy(buf + nick_len, nick, nick_len);
memcpy(buf + nick_len * 2, pass, pass_len);
output = base64_encode(buf, nick_len * 2 + pass_len);
my_send_to_server(from_server, "AUTHENTICATE %s", output);
new_free(&output);
}
}
void add_user_who (WhoEntry *w, char *from, char **ArgList)
{
char *userhost;
int op = 0, voice = 0;
/* Obviously this is safe. */
userhost = alloca(strlen(ArgList[1]) + strlen(ArgList[2]) + 2);
sprintf(userhost, "%s@%s", ArgList[1], ArgList[2]);
voice = (strchr(ArgList[5], '+') != NULL);
op = (strchr(ArgList[5], '@') != NULL);
add_to_channel(ArgList[0], ArgList[4], from_server, op, voice, userhost, ArgList[3], ArgList[5], 0, ArgList[6] ? my_atol(ArgList[6]) : 0);
#ifdef WANT_NSLOOKUP
if (get_int_var(AUTO_NSLOOKUP_VAR))
do_nslookup(ArgList[2], ArgList[4], ArgList[1], ArgList[0], from_server, auto_nslookup, NULL);
#endif
}
void add_user_end (WhoEntry *w, char *from, char **ArgList)
{
got_info(ArgList[0], from_server, GOTWHO);
/* Nothing to do! */
}
static void p_channel(char *from, char **ArgList)
{
char *channel;
ChannelList *chan = NULL;
NickList *tmpnick = NULL;
WhowasList *whowas = NULL;
int its_me = 0;
int op = 0, vo = 0;
char extra[80];
register char *c;
Window *old_window = current_window;
int switched = 0;
irc_server *irc_serv = NULL;
if (!strcmp(ArgList[0], zero))
{
fake();
return;
}
channel = ArgList[0];
set_display_target(channel, LOG_CRAP);
malloc_strcpy(&joined_nick, from);
/*
* Workaround for gratuitous protocol change in ef2.9
*/
*extra = 0;
if ((c = strchr(channel, '\007')))
{
for (*c++ = 0; *c; c++)
{
if (*c == 'o') op = 1;
else if (*c == 'v') vo = 1;
}
}
if (op)
strcat(extra, " (+o)");
if (vo)
strcat(extra, " (+v)");
if (!my_stricmp(from, get_server_nickname(from_server)))
{
int refnum;
if (!in_join_list(channel, from_server))
{
add_to_join_list(channel, from_server, current_window->refnum);
refnum = current_window->refnum;
}
else
{
if (current_window->refnum != (refnum = get_win_from_join_list(channel, from_server)))
{
switched = 1;
make_window_current(get_window_by_refnum(refnum));
}
}
its_me = 1;
chan = add_channel(channel, from_server, refnum);
do_hook(JOIN_ME_LIST, "%s %d", channel, refnum);
if (*channel == '+')
{
got_info(channel, from_server, GOTBANS);
got_info(channel, from_server, GOTMODE);
if ((get_server_version(from_server) == Server2_8ts4)
|| (get_server_version(from_server) == Server2_10))
got_info(channel, from_server, GOTEXEMPT);
}
else
{
int ver = get_server_version(from_server);
if ((ver == Server2_8ts4) || (ver == Server2_10))
send_to_server("MODE %s\r\nMODE %s b\r\nMODE %s e", channel, channel, channel);
else
send_to_server("MODE %s\r\nMODE %s b", channel, channel);
}
whobase(channel, add_user_who, add_user_end, NULL);
}
else
{
if ((whowas = check_whosplitin_buffer(from, FromUserHost, channel, 0)))
irc_serv = check_split_server(whowas->server1);
chan = add_to_channel(channel, from, from_server, op, vo, FromUserHost, NULL, NULL, whowas && irc_serv ? 1 : 0, 0);
if (whowas && whowas->server2 && irc_serv)
new_free(&whowas->server2);
#ifdef WANT_TCL
check_tcl_join(from, FromUserHost, from, channel);
#endif
logmsg(LOG_JOIN, from, 0, "%s %s %s", FromUserHost, channel, extra);
do_logchannel(LOG_JOIN, chan, "%s, %s %s %s", from, FromUserHost, channel, extra);
if (!irc_serv)
check_channel_limit(chan);
}
#ifdef WANT_USERLIST
if (!in_join_list(channel, from_server) && chan)
tmpnick = check_auto(channel, find_nicklist_in_channellist(from, chan, 0), chan);
#endif
flush_mode_all(chan);
if (tmpnick && !tmpnick->ip && get_int_var(AUTO_NSLOOKUP_VAR))
{
char *user;
#ifdef WANT_NSLOOKUP
char *host;
#endif
user = LOCAL_COPY(FromUserHost);
#ifdef WANT_NSLOOKUP
if ((host = strchr(user, '@')))
{
*host++ = 0;
do_nslookup(host, from, user, channel, from_server, auto_nslookup, NULL);
}
#endif
}
set_display_target(channel, LOG_CRAP);
if (whowas)
{
if (irc_serv)
{
if ((do_hook(LLOOK_JOIN_LIST, "%s %s", irc_serv->name, irc_serv->link)))
put_it("%s", convert_output_format(fget_string_var(FORMAT_NETJOIN_FSET), "%s %s %s %d", update_clock(GET_TIME), irc_serv->name, irc_serv->link, 0));
remove_split_server(CHAN_SPLIT, irc_serv->name);
}
#ifdef WANT_TCL
check_tcl_rejoin(from, FromUserHost, from, channel);
#endif
}
if (check_ignore(from, FromUserHost, channel, IGNORE_JOINS, NULL) != IGNORED && chan)
{
if (do_hook(JOIN_LIST, "%s %s %s %s", from, channel, FromUserHost? FromUserHost : "UnKnown", extra))
{
if (chan && (tmpnick = find_nicklist_in_channellist(from, chan, 0)))
{
if (tmpnick->userlist)
put_it("%s",convert_output_format(fget_string_var(FORMAT_FRIEND_JOIN_FSET), "%s %s %s %s %s %s",update_clock(GET_TIME),from,FromUserHost?FromUserHost:"UnKnown",channel, tmpnick->userlist?(tmpnick->userlist->comment?tmpnick->userlist->comment:empty_string):empty_string, extra));
else
put_it("%s",convert_output_format(fget_string_var(FORMAT_JOIN_FSET), "%s %s %s %s %s",update_clock(GET_TIME),from,FromUserHost?FromUserHost:"UnKnown",channel, extra));
}
else
put_it("%s",convert_output_format(fget_string_var(FORMAT_JOIN_FSET), "%s %s %s %s %s",update_clock(GET_TIME),from,FromUserHost?FromUserHost:"UnKnown",channel, extra));
if (!its_me && chan && chan->have_op)
{
if (get_cset_int_var(chan->csets, LAMELIST_CSET))
{
if (lame_list && find_in_list((List **)&lame_list, from, 0))
{
send_to_server("MODE %s -o+b %s %s!*", chan->channel, from, from);
send_to_server("KICK %s %s :\002Lame Nick detected\002", chan->channel, from);
if (get_int_var(AUTO_UNBAN_VAR))
add_timer(0, empty_string, get_int_var(AUTO_UNBAN_VAR) * 1000, 1, timer_unban, m_sprintf("%d %s %s!*", from_server, chan->channel, from), NULL, current_window->refnum, "auto-unban");
}
}
if (get_cset_int_var(chan->csets, LAMEIDENT_CSET))
{
/* This may be obsolete, I don't know of any servers that allow this */
static const char lame_chars[] =
"\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a";
if (strpbrk(FromUserHost, lame_chars))
{
char *host = strchr(FromUserHost, '@') + 1;
send_to_server("MODE %s +b *!*@%s\r\nKICK %s %s :\002Lame Ident detected\002", chan->channel, cluster(host), chan->channel, from);
}
}
}
}
}
reset_display_target();
#ifdef GUI
gui_update_nicklist(channel);
#endif
set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);
update_all_status(current_window, NULL, 0);
notify_mark(from, FromUserHost, 1, 0);
if (switched)
make_window_current(old_window);
}
void check_auto_join(int server, char *from, char *channel, char *key)
{
ChannelList *chan = NULL;
WhowasChanList *w_chan = NULL;
UserList *u = NULL;
CSetList *cset = NULL;
if (in_join_list(channel, from_server))
return;
if ((w_chan = check_whowas_chan_buffer(channel, -1, 0)))
{
chan = w_chan->channellist;
#ifdef WANT_USERLIST
if (((get_cset_int_var(chan->csets, AUTO_REJOIN_CSET)) || (!chan && get_int_var(AUTO_REJOIN_VAR))) && (channel && ((u = lookup_userlevelc(from, FromUserHost, channel, NULL)) != NULL)))
{
if ((u->flags & ADD_BOT))
goto got_request;
}
else
#endif
if (get_cset_int_var(chan->csets, AUTO_JOIN_ON_INVITE_CSET))
goto got_request;
}
else if ((cset = (CSetList *) check_cset_queue(channel, 0)))
{
if (get_cset_int_var(cset, AUTO_JOIN_ON_INVITE_CSET))
goto got_request;
}
return;
got_request:
bitchsay("Auto-joining %s on invite", channel);
add_to_join_list(channel, from_server, current_window->refnum);
send_to_server("JOIN %s", channel);
}
static void p_invite(char *from, char **ArgList)
{
char *high;
switch (check_ignore(from, FromUserHost, ArgList[1] ? ArgList[1] : NULL, IGNORE_INVITES, NULL))
{
case IGNORED:
if (get_int_var(SEND_IGNORE_MSG_VAR))
send_to_server("NOTICE %s :%s is ignoring you",
from, get_server_nickname(from_server));
return;
case HIGHLIGHTED:
high = highlight_char;
break;
default:
high = empty_string;
break;
}
if (ArgList[0] && ArgList[1])
{
ChannelList *chan = NULL;
set_display_target(from, LOG_CRAP);
malloc_strcpy(&invite_channel, ArgList[1]);
if (check_flooding(from, INVITE_FLOOD, ArgList[1], NULL) &&
do_hook(INVITE_LIST, "%s %s %s", from, ArgList[1], ArgList[2]?ArgList[2]:empty_string))
{
char *s;
put_it("%s", convert_output_format(fget_string_var(FORMAT_INVITE_FSET), "%s %s %s",update_clock(GET_TIME), from, ArgList[1]));
if ((s = convert_to_keystr("JOIN_LAST_INVITE")) && *s)
{
if (!get_int_var(AUTO_JOIN_ON_INVITE_VAR))
{
if (ArgList[2])
bitchsay("Press %s to join %s (%s)", s, invite_channel, ArgList[2]);
else
bitchsay("Press %s to join %s", s, invite_channel);
}
}
logmsg(LOG_INVITE, from, 0, "%s", invite_channel);
}
if (!(chan = lookup_channel(invite_channel, from_server, 0)))
check_auto_join(from_server, from, invite_channel, ArgList[2]);
add_last_type(&last_invite_channel[0], 1, from, FromUserHost, NULL, ArgList[1]);
reset_display_target();
}
}
static void p_silence (char *from, char **ArgList)
{
char *target = ArgList[0];
char *mag = target++;
if (do_hook(SILENCE_LIST, "%c %s", *mag, target))
put_it("%s", convert_output_format(fget_string_var(FORMAT_SILENCE_FSET), "%s %c %s", update_clock(GET_TIME), *mag, target));
}
static void p_kill(char *from, char **ArgList)
{
int port;
int localkill;
int serverkill = strchr(from, '.') != NULL;
int next_server;
char sc[20];
/*
* Bogorific Microsoft Exchange ``IRC'' server sends out a KILL
* protocol message instead of a QUIT protocol message when
* someone is killed on your server. Do the obviously appropriate
* thing and reroute this misdirected protocol message to
* p_quit, where it should have been sent in the first place.
* Die Microsoft, Die.
*/
if (!isme(ArgList[0]))
{
/* I don't care if this doesn't work. */
p_quit(from, ArgList); /* Die Microsoft, Die */
return;
}
port = get_server_port(from_server);
snprintf(sc, 19, "+%i %d", from_server, port);
localkill = !serverkill && ArgList[1] &&
strstr(ArgList[1], get_server_name(from_server));
next_server = localkill && get_int_var(NEXT_SERVER_ON_LOCAL_KILL_VAR);
if (serverkill || (get_int_var(AUTO_RECONNECT_VAR) && !next_server))
set_server_reconnecting(from_server, 1);
close_server(from_server,empty_string);
window_check_servers(from_server);
set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);
if (serverkill)
{
say("Server [%s] has rejected you (probably due to a nick collision)", from);
servercmd(NULL, sc, empty_string, NULL);
}
else
{
if (localkill)
{
int i = from_server + 1;
if (i >= server_list_size())
i = 0;
snprintf(sc, 19, "+%i", i);
from_server = -1;
}
if (do_hook(DISCONNECT_LIST,"Killed by %s (%s)",from,
ArgList[1] ? ArgList[1] : "(No Reason)"))
put_it("%s", convert_output_format(fget_string_var(FORMAT_KILL_FSET), "%s %s %s", update_clock(GET_TIME), from, ArgList[1]? ArgList[1] : "You have been Killed"));
if (get_int_var(CHANGE_NICK_ON_KILL_VAR))
fudge_nickname(from_server, 1);
if (get_int_var(AUTO_RECONNECT_VAR))
servercmd (NULL, sc, empty_string, NULL);
logmsg(LOG_KILL, from, 0, "%s", ArgList[1]?ArgList[1]:"(No Reason)");
}
update_all_status(current_window, NULL, 0);
}
static void p_ping(char *from, char **ArgList)
{
PasteArgs(ArgList, 0);
send_to_server("PONG %s", ArgList[0]);
}
static void p_nick(char *from, char **ArgList)
{
int one_prints = 0,
its_me = 0;
ChannelList *chan;
char *line;
line = ArgList[0];
if (!my_stricmp(from, get_server_nickname(from_server)))
{
accept_server_nickname(from_server, line);
its_me = 1;
nick_command_is_pending(from_server, 0);
}
if (check_ignore(from, FromUserHost, NULL, IGNORE_NICKS, NULL) == IGNORED)
goto do_nick_rename;
for (chan = get_server_channels(from_server); chan; chan = chan->next)
{
if (find_nicklist_in_channellist(from, chan, 0)) {
#ifdef WANT_TCL
if (!its_me)
check_tcl_nick(from, FromUserHost, from, chan->channel, line);
#endif
set_display_target(chan->channel, LOG_CRAP);
if (do_hook(CHANNEL_NICK_LIST, "%s %s %s", chan->channel, from, line))
one_prints = 1;
do_logchannel(LOG_CRAP, chan, "%s %s", from, line);
}
}
if (one_prints)
{
if (its_me)
{
set_string_var(AUTO_RESPONSE_STR_VAR, line);
reset_display_target();
} else
set_display_target(what_channel(from, from_server), LOG_CRAP);
if (do_hook(NICKNAME_LIST, "%s %s", from, line))
put_it("%s",convert_output_format(
fget_string_var(its_me?FORMAT_NICKNAME_USER_FSET:
im_on_channel(what_channel(from, from_server), from_server)?
FORMAT_NICKNAME_FSET:
FORMAT_NICKNAME_OTHER_FSET),
"%s %s %s %s",
update_clock(GET_TIME),from, "-", line));
}
do_nick_rename:
rename_nick(from, line, from_server);
#ifdef WANT_NSLOOKUP
ar_rename_nick(from, line, from_server);
#endif
if (!its_me)
{
notify_mark(from, FromUserHost, 0, 0);
notify_mark(line, FromUserHost, 1, 0);
}
#ifdef GUI
gui_update_nicklist(NULL);
#endif
}
static int check_mode_change(NickList *nick, char type_mode, char *from, char *this_nick, char *channel)
{
time_t right_now = now;
int found = 0;
if (!nick->userlist && !isme(nick->nick))
{
if ((!nick_isop(nick) && type_mode == '+') || (nick_isop(nick) && type_mode == '-'))
{
switch(type_mode)
{
case '-':
if (nick->sent_deop > 4 && right_now - nick->sent_deop_time < 10)
return 0;
nick->sent_deop++;
nick->sent_deop_time = right_now;
break;
case '+':
if (nick->sent_reop > 4 && right_now - nick->sent_reop_time < 10)
return 0;
nick->sent_reop++;
nick->sent_reop_time = right_now;
break;
default:
break;
}
if (my_stricmp(this_nick, from))
{
send_to_server("MODE %s %co %s", channel, type_mode, this_nick);
found++;
}
}
}
return found;
}
static void check_bitch_mode(char *from, char *uh, char *channel, char *line, ChannelList *chan)
{
NickList *nick;
char *new_mode = NULL;
char *n = NULL;
if (!from || !chan || (chan && (!get_cset_int_var(chan->csets, BITCH_CSET) || !chan->have_op)))
return;
if (!get_int_var(HACK_OPS_VAR) && wild_match("%.%", from))
return;
if (!(nick = find_nicklist_in_channellist(from, chan, 0)))
return;
set_display_target(channel, LOG_CRAP);
new_mode = LOCAL_COPY(line);
new_mode = next_arg(new_mode, &n);
if (!nick->userlist || !check_channel_match(nick->userlist->channels, channel))
{
char *p;
char type_mode = '%' , *this_nick, *list_nicks;
int found = 0;
list_nicks = LOCAL_COPY(n);
for (p = new_mode; *p; p++)
{
switch(*p)
{
case '-':
type_mode = '+';
break;
case '+':
type_mode = '-';
break;
case 'o':
this_nick = next_arg(list_nicks, &list_nicks);
nick = find_nicklist_in_channellist(this_nick, chan, 0);
found += check_mode_change(nick, type_mode, from, this_nick, channel);
break;
default:
break;
}
}
if (found)
put_it("%s", convert_output_format(fget_string_var(FORMAT_BITCH_FSET), "%s %s %s %s %s %s", update_clock(GET_TIME), from, uh, channel, new_mode, n));
}
reset_display_target();
}
static void p_mode(char *from, char **ArgList)
{
char *target;
char *line;
int flag;
ChannelList *chan = NULL;
ChannelList *chan2 = get_server_channels(from_server);
char buffer[BIG_BUFFER_SIZE+1];
char *smode;
char *display_uh = FromUserHost[0] ? FromUserHost : "*";
#ifdef COMPRESS_MODES
char *tmpbuf = NULL;
#endif
PasteArgs(ArgList, 1);
target = ArgList[0];
line = ArgList[1];
smode = strchr(from, '.');
flag = check_ignore(from, FromUserHost, target, (smode?IGNORE_SMODES : IGNORE_MODES) | IGNORE_CRAP, NULL);
if (target && line)
{
strcpy(buffer, line);
if (get_int_var(MODE_STRIPPER_VAR))
strip_modes(from, target, line);
if (is_channel(target))
{
set_display_target(target, LOG_MODE_CHAN);
#ifdef COMPRESS_MODES
if (chan2)
chan = (ChannelList *)find_in_list((List **)&chan2, target, 0);
if (chan && get_cset_int_var(chan->csets, COMPRESS_MODES_CSET))
{
tmpbuf = do_compress_modes(chan, from_server, target, line);
if (tmpbuf)
strcpy(line, tmpbuf);
else
flag = IGNORED;
}
#endif
/* CDE handle mode protection here instead of later */
update_channel_mode(from, target, from_server, buffer, chan);
#ifdef WANT_TCL
check_tcl_mode(from, FromUserHost, from, target, line);
#endif
if (my_stricmp(from, get_server_nickname(from_server)))
{
check_mode_lock(target, line, from_server);
check_bitch_mode(from, FromUserHost, target, line, chan);
}
if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s", from, target, line))
{
enum FSET_TYPES fset = smode ? FORMAT_SMODE_FSET : FORMAT_MODE_FSET;
put_it("%s", convert_output_format(fget_string_var(fset), "%s %s %s %s %s", update_clock(GET_TIME), from, display_uh, target, line));
}
logmsg(LOG_MODE_CHAN, from, 0, "%s %s", target, line);
do_logchannel(LOG_MODE_CHAN, chan, "%s %s, %s", from, target, line);
}
else
{
set_display_target(target, LOG_MODE_USER);
if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s", from, target, line))
{
/* User mode changes where from != target don't occur on
* standard servers, but are used by services on some networks. */
enum FSET_TYPES fset = my_stricmp(from, target) ? FORMAT_USERMODE_OTHER_FSET : FORMAT_USERMODE_FSET;
put_it("%s", convert_output_format(fget_string_var(fset), "%s %s %s %s %s", update_clock(GET_TIME), from, display_uh, target, line));
}
if (!my_stricmp(target, get_server_nickname(from_server)))
update_user_mode(line);
logmsg(LOG_MODE_USER, from, 0, "%s %s", target, line);
}
update_all_status(current_window, NULL, 0);
}
#ifdef GUI
gui_update_nicklist(target);
#endif
reset_display_target();
}
static void strip_modes (char *from, char *channel, char *line)
{
char *mode;
char *pointer;
char mag = '+'; /* XXXX Bogus */
char *copy = NULL;
char free_copy[BIG_BUFFER_SIZE+1];
strcpy(free_copy, line);
copy = free_copy;
mode = next_arg(copy, &copy);
if (is_channel(channel))
{
for (pointer = mode; *pointer; pointer++)
{
char c = *pointer;
switch (c)
{
case '+' :
case '-' : mag = c; break;
case 'l' : if (mag == '+')
do_hook(MODE_STRIPPED_LIST,"%s %s %c%c %s",
from,channel,mag,c,next_arg(copy,&copy));
else
do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",
from,channel,mag,c);
break;
case 'a' :
case 'i' :
case 'm' :
case 'n' :
case 'p' :
case 's' :
case 't' :
case 'z' :
case 'c' :
case 'r' :
case 'R' :
do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",from,
channel,mag,c);
break;
case 'b' :
case 'k' :
case 'o' :
case 'e' :
case 'I' :
case 'v' : do_hook(MODE_STRIPPED_LIST,"%s %s %c%c %s",from,
channel,mag,c,next_arg(copy,&copy));
break;
}
}
}
else /* User mode */
{
for (pointer = mode; *pointer; pointer++)
{
char c = *pointer;
switch (c)
{
case '+' :
case '-' : mag = c; break;
default : do_hook(MODE_STRIPPED_LIST,"%s %s %c%c",from, channel, mag, c);
break;
}
}
}
}
static void p_kick(char *from, char **ArgList)
{
char *channel = ArgList[0];
char *target = ArgList[1];
char *comment = ArgList[2] ? ArgList[2] : "(no comment)";
char *chankey = NULL;
ChannelList *chan = NULL;
NickList *from_nick = NULL;
int t = 0;
if ((chan = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
from_nick = find_nicklist_in_channellist(from, chan, 0);
set_display_target(channel, LOG_CRAP);
if (channel && target && chan)
{
update_stats(KICKLIST, from_nick, chan, 0);
#ifdef WANT_TCL
check_tcl_kick(from, FromUserHost, from, channel, target, comment);
#endif
if (!my_stricmp(target, get_server_nickname(from_server)))
{
Window *window = get_window_by_refnum(chan->refnum);/*chan->window;*/
int rejoin = 0;
if (chan->key)
malloc_strcpy(&chankey, chan->key);
rejoin = get_cset_int_var(chan->csets, AUTO_REJOIN_CSET);
switch(rejoin)
{
case 0:
case 1:
break;
case 2:
if (FromUserHost)
{
char *username;
char *ptr;
username = LOCAL_COPY(FromUserHost);
if ((ptr = strchr(username, '@')))
{
*ptr = 0;
ptr = clear_server_flags(username);
} else
ptr = username;
do_newuser(NULL, ptr, NULL);
}
break;
case 3:
send_to_server("NICK %s", random_str(3,9));
break;
case 4:
do_newuser(NULL, random_str(2,9), NULL);
case 5:
default:
send_to_server("NICK %s", random_str(3,9));
break;
}
do_logchannel(LOG_KICK_USER, chan, "%s %s, %s %s %s", from, FromUserHost, target, channel, comment);
if (rejoin)
send_to_server("JOIN %s%s%s", channel, chankey? space : empty_string, chankey ? chankey: empty_string);
new_free(&chankey);
if (do_hook(KICK_LIST, "%s %s %s %s", target, from, channel, comment?comment:empty_string))
put_it("%s",convert_output_format(fget_string_var(FORMAT_KICK_USER_FSET),"%s %s %s %s %s",update_clock(GET_TIME),from, channel, target, comment));
remove_channel(channel);
update_all_status(window ? window : current_window, NULL, 0);
update_input(UPDATE_ALL);
logmsg(LOG_KICK_USER, from, 0, "%s %s %s %s", FromUserHost, target, channel, comment);
if (rejoin)
add_to_join_list(channel, from_server, window ? window->refnum : 0);
}
else
{
NickList *f_nick = NULL;
int itsme = !my_stricmp(get_server_nickname(from_server), from);
if ((check_ignore(from, FromUserHost, channel, IGNORE_KICKS, NULL) != IGNORED) &&
do_hook(KICK_LIST, "%s %s %s %s", target, from, channel, comment))
put_it("%s",convert_output_format(fget_string_var(FORMAT_KICK_FSET),"%s %s %s %s %s",update_clock(GET_TIME),from, channel, target, comment));
/* if it's me that's doing the kick don't flood check */
if (!itsme)
{
f_nick = find_nicklist_in_channellist(target, chan, 0);
if (chan->have_op && from_nick && is_other_flood(chan, from_nick, KICK_FLOOD, &t))
{
if (get_cset_int_var(chan->csets, KICK_ON_KICKFLOOD_CSET) > get_cset_int_var(chan->csets, DEOP_ON_KICKFLOOD_CSET))
send_to_server("MODE %s -o %s", chan->channel, from);
else if (!from_nick->sent_kick++)
send_to_server("KICK %s %s :\002Mass kick detected - (%d kicks in %dsec%s)\002", chan->channel, from, get_cset_int_var(chan->csets, KICK_ON_KICKFLOOD_CSET), t, plural(t));
}
#ifdef WANT_USERLIST
check_prot(from, target, chan, NULL, f_nick);
#endif
}
remove_from_channel(channel, target, from_server, 0, NULL);
logmsg(LOG_KICK, from, 0, "%s %s %s %s", FromUserHost, target, channel, comment);
do_logchannel(LOG_KICK, chan, "%s %s %s %s %s", from, FromUserHost, target, channel, comment);
}
}
update_all_status(current_window, NULL, 0);
reset_display_target();
#ifdef GUI
gui_update_nicklist(channel);
#endif
}
static void p_part(char *from, char **ArgList)
{
char *channel;
ChannelList *tmpc;
if (!from || !*from)
return;
channel = ArgList[0];
PasteArgs(ArgList, 1);
set_display_target(channel, LOG_CRAP);
if ((tmpc = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
update_stats(LEAVELIST, find_nicklist_in_channellist(from, tmpc, 0), tmpc, 0);
if ((check_ignore(from, FromUserHost, channel, IGNORE_PARTS, NULL) != IGNORED) &&
do_hook(LEAVE_LIST, "%s %s %s %s", from, channel, FromUserHost, ArgList[1]?ArgList[1]:empty_string))
put_it("%s",convert_output_format(fget_string_var(FORMAT_LEAVE_FSET), "%s %s %s %s %s", update_clock(GET_TIME), from, FromUserHost, channel, ArgList[1]?ArgList[1]:empty_string));
if (!my_stricmp(from, get_server_nickname(from_server)))
{
remove_channel(channel);
remove_from_mode_list(channel, from_server);
remove_from_join_list(channel, from_server);
set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);
do_hook(LEAVE_ME_LIST, "%s", channel);
}
else
{
#ifdef WANT_TCL
check_tcl_part(from, FromUserHost, from, channel);
#endif
remove_from_channel(channel, from, from_server, 0, NULL);
logmsg(LOG_PART, from, 0, "%s %s", channel, ArgList[1] ? ArgList[1]:empty_string);
do_logchannel(LOG_PART, tmpc, "%s %s %s", channel, from, ArgList[1] ? ArgList[1]:empty_string);
}
update_all_status(current_window, NULL, 0);
update_input(UPDATE_ALL);
reset_display_target();
#ifdef GUI
gui_update_nicklist(channel);
#endif
}
static void rfc1459_odd (char *from, char *comm, char **ArgList)
{
PasteArgs(ArgList, 0);
if (do_hook(ODD_SERVER_STUFF_LIST, "%s %s %s", from ? from : "*", comm, ArgList[0]))
{
if (from)
say("Odd server stuff: \"%s %s\" (%s)", comm, ArgList[0], from);
else
say("Odd server stuff: \"%s %s\"", comm, ArgList[0]);
}
}
static void p_rpong (char *from, char **ArgList)
{
if (!ArgList[3])
{
PasteArgs(ArgList, 0);
say("RPONG %s (from %s)", ArgList[0], from);
}
else
{
time_t delay = now - atol(ArgList[3]);
say("Pingtime %s - %s : %s ms (total delay: %ld s)",
from, ArgList[1], ArgList[2], delay);
}
}
protocol_command rfc1459[] = {
{ "ADMIN", NULL, NULL, 0, 0, 0},
{ "AUTHENTICATE", p_authenticate, NULL, 0, 0, 0},
{ "AWAY", NULL, NULL, 0, 0, 0},
{ "CAP", p_cap, NULL, 0, 0, 0},
{ "CONNECT", NULL, NULL, 0, 0, 0},
{ "ERROR", p_error, NULL, 0, 0, 0},
{ "ERROR:", p_error, NULL, 0, 0, 0},
{ "INFO", NULL, NULL, 0, 0, 0},
{ "INVITE", p_invite, NULL, 0, 0, 0},
{ "ISON", NULL, NULL, PROTO_NOQUOTE, 0, 0},
{ "JOIN", p_channel, NULL, PROTO_DEPREC, 0, 0},
{ "KICK", p_kick, NULL, 0, 0, 0},
{ "KILL", p_kill, NULL, 0, 0, 0},
{ "LINKS", NULL, NULL, 0, 0, 0},
{ "LIST", NULL, NULL, 0, 0, 0},
{ "MODE", p_mode, NULL, 0, 0, 0},
{ "NAMES", NULL, NULL, 0, 0, 0},
{ "NICK", p_nick, NULL, PROTO_NOQUOTE, 0, 0},
{ "NOTICE", parse_notice, NULL, 0, 0, 0},
{ "OPER", NULL, NULL, 0, 0, 0},
{ "PART", p_part, NULL, PROTO_DEPREC, 0, 0},
{ "PASS", NULL, NULL, 0, 0, 0},
{ "PING", p_ping, NULL, 0, 0, 0},
{ "PONG", p_pong, NULL, 0, 0, 0},
{ "PRIVMSG", p_privmsg, NULL, 0, 0, 0},
{ "QUIT", p_quit, NULL, PROTO_DEPREC, 0, 0},
{ "REHASH", NULL, NULL, 0, 0, 0},
{ "RESTART", NULL, NULL, 0, 0, 0},
{ "RPONG", p_rpong, NULL, 0, 0, 0},
{ "SERVER", NULL, NULL, PROTO_NOQUOTE, 0, 0},
{ "SILENCE", p_silence, NULL, 0, 0, 0},
{ "SQUIT", NULL, NULL, 0, 0, 0},
{ "STATS", NULL, NULL, 0, 0, 0},
{ "SUMMON", NULL, NULL, 0, 0, 0},
{ "TIME", NULL, NULL, 0, 0, 0},
{ "TOPIC", p_topic, NULL, 0, 0, 0},
{ "TRACE", NULL, NULL, 0, 0, 0},
{ "USER", NULL, NULL, 0, 0, 0},
{ "USERHOST", NULL, NULL, PROTO_NOQUOTE, 0, 0},
{ "USERS", NULL, NULL, 0, 0, 0},
{ "VERSION", NULL, NULL, 0, 0, 0},
{ "WALLOPS", p_wallops, NULL, 0, 0, 0},
{ "WHO", NULL, NULL, PROTO_NOQUOTE, 0, 0},
{ "WHOIS", NULL, NULL, 0, 0, 0},
{ "WHOWAS", NULL, NULL, 0, 0, 0},
{ NULL, NULL, NULL, 0, 0, 0}
};
#define NUMBER_OF_COMMANDS (sizeof(rfc1459) / sizeof(protocol_command)) - 2;
int num_protocol_cmds = -1;
BUILT_IN_COMMAND(debugmsg)
{
int i;
unsigned long total = 0;
for (i = 0; rfc1459[i].command; i++)
{
put_it("DEBUG_MSG: %10s[%03lu] # %ld -> %ld bytes", rfc1459[i].command, i, rfc1459[i].count, rfc1459[i].bytes);
total += rfc1459[i].bytes;
}
put_it("DEBUG_MSG: Total bytes received %ld", total);
}
void parse_server(char *orig_line)
{
char *from,
*comm,
*end;
int numeric;
char *line = NULL;
int len = 0;
char **ArgList;
char copy[BIG_BUFFER_SIZE+1];
char *TrueArgs[MAXPARA + 1] = {NULL};
#ifdef WANT_DLL
RawDll *raw = NULL;
#endif
int loc;
int cnt;
if (num_protocol_cmds == -1)
num_protocol_cmds = NUMBER_OF_COMMANDS;
if (!orig_line || !*orig_line)
return;
len = strlen(orig_line);
end = len + orig_line;
if (*--end == '\n')
*end-- = 0;
if (*end == '\r')
*end-- = 0;
if (x_debug & DEBUG_INBOUND)
yell("[%d] <- [%s]", get_server_read(from_server), orig_line);
if (*orig_line == ':')
{
if (!do_hook(RAW_IRC_LIST, "%s", orig_line + 1))
return;
}
else if (!do_hook(RAW_IRC_LIST, "* %s", orig_line))
return;
if (inbound_line_mangler)
{
len = strlen(orig_line) * 3;
line = alloca(len + 1);
strcpy(line, orig_line);
if (mangle_line(line, inbound_line_mangler, len) > len)
yell("mangle_line truncated its result. Ack.");
}
else
line = orig_line;
ArgList = TrueArgs;
strncpy(copy, line, BIG_BUFFER_SIZE);
BreakArgs(line, &from, ArgList, 0);
/* XXXX - i dont think 'from' can be null here. */
if (!(comm = (*ArgList++)) || !from || !*ArgList)
return; /* Serious protocol violation -- ByeBye */
/* Check for egregiously bad nicknames */
#define islegal(c) (((c) >= 'A' && (c) <= '~') || \
((c) >= '0' && (c) <= '9') || (c) == '*' || (c & 0x80))
if (*from && (!islegal(*from) || strchr(from, ',')))
{
rfc1459_odd(from, comm, ArgList);
return;
}
#ifdef WANT_TCL
if (check_tcl_raw(copy, comm))
return;
#endif
#ifdef WANT_DLL
if ((raw = find_raw_proc(comm, ArgList)))
if ((raw->func(comm, from, FromUserHost, ArgList)))
return;
#endif
#if 0
if (translation)
{
unsigned char *q, *p;
int i = 0;
q = ArgList[0];
while (q && *q)
{
for (p = q; *p; p++)
*p = transToClient[(int)*p];
q = ArgList[++i];
}
}
#endif
/*
* I reformatted these in may '96 by using the output of /stats m
* from a few busy servers. They are arranged so that the most
* common types are high on the list (to save the average number
* of compares.) I will be doing more testing in the future on
* a live client to see if this is a reasonable order.
*/
if ((numeric = atoi(comm)) > 0) /* numbered_command can't handle -ves */
numbered_command(from, numeric, ArgList);
else
{
find_fixed_array_item((void *)rfc1459, sizeof(protocol_command),
num_protocol_cmds + 1, comm, &cnt, &loc);
if (cnt < 0 && rfc1459[loc].inbound_handler)
rfc1459[loc].inbound_handler(from, ArgList);
else
rfc1459_odd(from, comm, ArgList);
rfc1459[loc].bytes += len;
rfc1459[loc].count++;
}
FromUserHost = empty_string;
from_server = -1;
}