#ifdef RCS
static char rcsid[]="$Id: ctcp.c,v 1.1.1.1 2000/11/13 02:42:41 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/ctcp.c,v $
* $Revision: 1.1.1.1 $
* $Date: 2000/11/13 02:42:41 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "function.h"
#include "user.h"
#include "transfer.h"
#include "flood.h"
#include "netstuff.h"
#include "ctcp.h"
#include "ibcp.h"
#include "fplrun.h"
#include "link.h"
/* --- Global ----------------------------------------------------- */
extern time_t now;
extern char channel[];
extern char nickname[], ctcpversion[], ctcpuserinfo[];
extern char opaction[];
extern bool avalance, uppercheck;
extern bool floodmode, xdccmode, repeatmode;
extern bool ctcpmode, shuttingdown;
extern bool public;
extern int serverport;
extern int opactionlen;
extern long levels[];
bool ctcpignore = FALSE;
int ctcpcount = 0;
int ctcpignorednum = 0;
time_t lastctcptime = 0;
time_t ctcpignorestart = 0;
time_t ctcptimeout; /* the actual time-out, no more replies for a while */
/* --- Private CTCPs ---------------------------------------------- */
struct
{
char *name;
void (*function)(char *a, char *b);
} ctcps[] =
{
{"PING", CtcpPing},
{"DCC", CtcpDCC},
{"CLIENTINFO", CtcpClientinfo},
{"VERSION", CtcpVersion},
{"TIME", CtcpTime},
{"USERINFO", CtcpUserinfo},
{NULL, (void(*)())(NULL)}
};
/* --- Public CTCPs ----------------------------------------------- */
struct
{
char *name;
void (*function)(char *a, char *b);
} pubctcps[] =
{
{"ACTION", CtcpAction},
{"PING", CtcpPing},
{"CLIENTINFO", CtcpClientinfo},
{"VERSION", CtcpVersion},
{"TIME", CtcpTime},
{"USERINFO", CtcpUserinfo},
{"XDCC", CtcpXdcc},
{NULL, (void(*)())(NULL)}
};
/* --- IgnoreCtcp ------------------------------------------------- */
/*
* This deals with private CTCP commands to the bot. CTCPFloodCheck()
* takes care of the ones in the channel.
*/
bool IgnoreCtcp(char *from, char *cmd)
{
/* Got more than 3 requests within a certain time */
if ((now - lastctcptime) <= 3) {
if (++ctcpcount > 2) {
lastctcptime = now;
if (!ctcpignore) {
char buf[BIGBUFFER];
StrFormatMax(buf, sizeof(buf), "Flood: Ignoring %s flood by %s", cmd, from);
Multicast(REPORTCAST, buf);
Log(LOGCTCP, buf);
}
CTCPignore();
ctcpignore = TRUE;
return TRUE; /* Ignore request */
}
}
else
ctcpcount = 0;
lastctcptime = now;
ctcpignore = FALSE;
return FALSE;
}
void CTCPignore(void)
{
if (0 == ctcpignorednum)
ctcpignorestart = now;
ctcptimeout = now + TIME_CTCPIGNORE;
ctcpignorednum++;
}
/* --- CTCP ------------------------------------------------------- */
void CTCP(char *from, char *line)
{
char nick[NICKLEN+1], msg[BIGBUFFER];
char *cmd, *param;
int i;
itemguest *g;
snapshot;
CtcpUnquote(msg, sizeof(msg), line);
#ifdef HAVE_LIBFPL
if (runfpl(RUN_CTCPPRIV, msg, FPLRUN_PRE))
return;
#endif
cmd = StrTokenize(msg, " ");
if (cmd) {
param = StrTokenize(NULL, "");
/*
* We will always let LEVELCHANOP+ users' DCC chat requests through!
* (when they're in the channel with us)
*/
if (StrEqual(cmd, "DCC") &&
(1 == StrScan(from, "%"NICKLENTXT"[^!]", nick)) &&
(g = FindNick(nick)) &&
(g->ident->level >= LEVELCHANOP))
;
else {
if (now < ctcptimeout) {
/* Ignoring CTCPs */
CTCPignore();
/*Logf(LOGCTCP, "Ignores %s from %s", cmd, from);*/
return; /* ignored */
}
else if (IgnoreCtcp(from, cmd)) {
return;
}
if (ctcpignorednum > 0) {
Logf(LOGCTCP, "Ignored %d CTCP requests in %s at the last flood",
ctcpignorednum,
TimeAgo(ctcptimeout - TIME_CTCPIGNORE - ctcpignorestart + now));
ctcpignorednum = 0;
}
}
/* Handle normal private ctcp request */
for (i=0; ctcps[i].name; i++) {
if (StrEqual(ctcps[i].name, cmd)) {
ctcps[i].function(from, param);
break;
}
}
}
runfpl(RUN_CTCPPRIV, msg, FPLRUN_POST);
}
/* --- PubCTCP ---------------------------------------------------- */
void PubCTCP(char *from, char *line)
{
char nick[NICKLEN+1], msg[BIGBUFFER];
char *cmd, *param;
int i;
itemguest *g;
snapshot;
CtcpUnquote(msg, sizeof(msg), line);
#ifdef HAVE_LIBFPL
if (runfpl(RUN_CTCPPUB, msg, FPLRUN_PRE))
return;
#endif
if (1 == StrScan(from, "%"NICKLENTXT"[^!]", nick)) {
g = FindNick(nick);
if (g) {
/* Handle public CTCP avalances */
if (avalance)
AvalanceCheck(g, msg);
}
/* Slower flood check on ACTION */
if (CTCPFloodCheck(g, StrEqualMax(msg, 6, "ACTION")) ||
(now < ctcptimeout)) {
/* ignoring CTCPs */
CTCPignore();
/*Logf(LOGCTCP, "Ignores channel '%s' from %s", msg, from);*/
return;
}
if (ctcpignorednum > 0) {
Logf(LOGCTCP, "Ignored %d channel CTCP requests in %s at the last flood",
ctcpignorednum,
TimeAgo(ctcptimeout - TIME_CTCPIGNORE - ctcpignorestart + now));
ctcpignorednum = 0;
}
cmd = StrTokenize(msg, " ");
if (cmd) {
param = StrTokenize(NULL, "");
for (i=0; pubctcps[i].name; i++) {
if (StrEqual(pubctcps[i].name, cmd)) {
pubctcps[i].function(from, param);
break;
}
}
}
}
runfpl(RUN_CTCPPUB, msg, FPLRUN_POST);
}
/* --- CtcpXdcc --------------------------------------------------- */
void CtcpXdcc(char *from, char *msg)
{
if (xdccmode) {
itemguest *g;
StrTokenize(from, "!");
g = FindNick(from);
if (g)
StickyKick(g, "xdcc this!");
}
}
/* --- CtcpClientinfo --------------------------------------------- */
void CtcpClientinfo(char *from, char *msg)
{
if (public)
Logf(LOGCTCP, "Clientinfo on %s from %s", channel, from);
else
Logf(LOGCTCP, "Clientinfo from %s", from);
StrTokenize(from, "!");
ReplyCtcpf(from, "CLIENTINFO %s", CLIENTINFOMSG);
}
/* --- CtcpPing --------------------------------------------------- */
void CtcpPing(char *from, char *msg)
{
if (public)
Logf(LOGCTCP, "Ping on %s from %s", channel, from);
else
Logf(LOGCTCP, "Ping from %s", from);
StrTokenize(from, "!");
if (msg && *msg)
ReplyCtcpf(from, "PING %s", msg);
else
ReplyCtcp(from, "ERRMSG Incorrect Ping request - time missing");
}
/* --- CtcpVersion ------------------------------------------------ */
void CtcpVersion(char *from, char *msg)
{
if (public)
Logf(LOGCTCP, "Version on %s from %s", channel, from);
else
Logf(LOGCTCP, "Version from %s", from);
StrTokenize(from, "!");
ReplyCtcpf(from, "VERSION %s", ctcpversion);
}
/* --- CtcpUserinfo ----------------------------------------------- */
void CtcpUserinfo(char *from, char *msg)
{
if (public)
Logf(LOGCTCP, "Userinfo on %s from %s", channel, from);
else
Logf(LOGCTCP, "Userinfo from %s", from);
StrTokenize(from, "!");
ReplyCtcpf(from, "USERINFO %s", ctcpuserinfo);
}
/* --- CtcpTime --------------------------------------------------- */
void CtcpTime(char *from, char *msg)
{
char timebuf[MIDBUFFER];
time_t time = now;
if (public)
Logf(LOGCTCP, "Time on %s from %s", channel, from);
else
Logf(LOGCTCP, "Time from %s", from);
StrTokenize(from, "!");
if (time != -1) {
StrCopyMax(timebuf, sizeof(timebuf), ctime(&time));
StrTokenize(timebuf, "\n"); /* ctime() appends a \n, get rid of it */
ReplyCtcpf(from, "TIME %s", timebuf);
}
else
ReplyCtcp(from, "ERRMSG Unable to get systemtime");
}
/* --- CtcpAction ------------------------------------------------- */
void CtcpAction(char *from, char *msg)
{
itemguest *g;
#ifdef HAVE_LIBFPL
if (runfpl(RUN_ACTION, msg, FPLRUN_PRE))
return;
#endif
StrTokenize(from, "!");
g = FindNick(from);
if (g) {
if (opaction[0]) { /* A kind of passive auto-op */
if ((g->ident->level >= LEVELBOT) &&
StrEqualMax(opaction, opactionlen, msg)) {
Mode("+o %s", from);
}
}
g->posts++;
Check(g, msg);
if (repeatmode)
RepeatCheck(g, msg);
g->posttime = now;
Logf(LOGPUB, "* %s %s", from, msg ? msg : "");
}
runfpl(RUN_ACTION, msg, FPLRUN_POST);
}
/* --- OpenDccChat ------------------------------------------------ */
void OpenDccChat(char *from, char *userhost, ulong num, ushort port)
{
itemuser *u;
itemclient *k;
u = FindUser(from, userhost);
if (u) {
k = FindClientByNick(from);
if (k) { /* Only one client per nick */
if (k->status == CL_CONNECTED)
ReplyCtcp(from, "ERRMSG Another connection already exist");
else
ReplyCtcp(from, "ERRMSG Another attempt to connect exist. Use CUT to cancel that attempt");
}
else
OpenClientConnection(from, userhost, u, MakeIP(num), port);
}
else /* Only allow registered users */
ReplyCtcp(from, "ERRMSG No authentication");
}
/* --- OpenUdpLink ------------------------------------------------ */
void OpenUdpLink(char *from, char *userhost, ulong num, ushort port)
{
itemuser *u;
itemlink *r;
u = FindUser(from, userhost);
if (u && u->flags.linkbot) {
r = AddLink(from, u, num, port);
if (r) {
r->tmpvalue = Random();
SendLink(r, IBCP_HELLO, "%d %d %s %lu",
IBCP_VERSION, IBCP_BOT_DANCER, nickname, r->tmpvalue);
}
else
ReplyCtcp(from, "ERRMSG Insufficient resources");
}
else
ReplyCtcp(from, "ERRMSG No authentication");
}
/* --- CtcpDCC ---------------------------------------------------- */
void CtcpDCC(char *from, char *msg)
{
char darg[BIGBUFFER];
char snum[MIDBUFFER];
char sport[MIDBUFFER];
char type[5];
char *userhost;
ushort port;
if (msg && *msg) {
Logf(LOGCTCP, "DCC request from %s (%s)", from, msg);
StrTokenize(from, "!");
userhost = StrTokenize(NULL, "");
if (shuttingdown) {
ReplyCtcp(from, "ERRMSG Shutdown in progress. Cannot accept connections");
return; /* Make sure we exit */
}
if (4 == StrScan(msg, "%4s %"BIGBUFFERTXT"s %"MIDBUFFERTXT"s %"MIDBUFFERTXT"s",
type, darg, snum, sport)) {
port = atol(sport);
if (port == serverport) {
/* User sucks, don't even answer */
Debug("%s (%s) attempted to DCC to serverport (%d)",
from, userhost, port);
}
else if (port < 1024) {
ReplyCtcp(from, "ERRMSG Connection rejected. Privileged port");
Debug("%s (%s) rejected - privileged port (%d)",
from, userhost, port);
}
else {
if (StrEqual(type, "CHAT")) {
/* darg ought to be "chat", but some clients use username
instead. We'll just ignore it. */
OpenDccChat(from, userhost, atoul(snum), port);
}
else if (StrEqual(type, "LINK") && StrEqual(darg, "UDP")) {
OpenUdpLink(from, userhost, atoul(snum), port);
}
else {
ReplyCtcp(from, "ERRMSG Unsupported DCC request");
Debug("Unsupported DCC request: %s %s", type, darg);
}
}
}
else {
ReplyCtcp(from, "ERRMSG Erroneous DCC request");
Debug("Wrong number of DCC arguments");
}
}
}
/* --- Quoting ---------------------------------------------------- */
#define CTCPQUOTE '\020'
#define CTCPQUOTE_FIRST '\000'
#define CTCPQUOTE_LAST '\037' /* Should be \040 */
#define CTCPQUOTE_OFFSET '\101'
void CtcpQuote(char *target, size_t len, char *source)
{
snapshot;
#if 0
register char c;
while (c = *source++) {
if (c == CTCPQUOTE)
*target++ = CTCPQUOTE;
else if ((c >= CTCPQUOTE_FIRST) && (c <= CTCPQUOTE_LAST)) {
*target++ = CTCPQUOTE;
c += CTCPQUOTE_OFFSET;
}
*target++ = c;
}
*target = (char)0;
#else
StrCopyMax(target, len, source);
#endif
}
void CtcpUnquote(char *target, size_t len, char *source)
{
snapshot;
#if 0
register char c;
while (c = *source++) {
if (c == CTCPQUOTE) {
c = *source++;
if ((c >= (CTCPQUOTE_FIRST + CTCPQUOTE_OFFSET)) &&
(c <= (CTCPQUOTE_LAST + CTCPQUOTE_OFFSET)))
c -= CTCPQUOTE_OFFSET;
}
*target++ = c;
}
*target = (char)0;
#else
StrCopyMax(target, len, source);
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1