#ifdef RCS
static char rcsid[]="$Id: transfer.c,v 1.1.1.1 2000/11/13 02:42:49 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/transfer.c,v $
* $Revision: 1.1.1.1 $
* $Date: 2000/11/13 02:42:49 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "function.h"
#include "user.h"
#include "transfer.h"
#include "netstuff.h"
#include "seen.h"
#include "servfunc.h"
#include "link.h"
#include "flood.h"
#include "bans.h"
#include "command.h"
#include "ctcp.h"
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
extern time_t now;
extern char nickname[];
extern char channel[];
extern char logfile[];
extern char botmatch[];
extern bool botop;
extern bool chat;
extern bool connected;
extern bool cleanup;
extern bool restart;
extern bool execprotect;
extern int lastpost;
extern fd_set rdset;
extern itemclient *client;
extern itemclient *clientHead;
extern itemguest *guestHead;
extern itemident *current;
struct Execstruct ExecHead = {
NULL, NULL, NULL, NULL, -1, NULL, FALSE
};
struct Msgstruct MsgHead = {
NULL, NULL, NULL, NULL
};
int kickQ = 0; /* Number of kicks in the queue */
itemexec *execHead = &ExecHead;
itemmsg *msgHead = &MsgHead;
/* --- SendNick --------------------------------------------------- */
/* Send directly to nick */
inline void SendNick(char *nick, char *msg)
{
itemmsg *m;
snapshot;
m = NewEntry(itemmsg);
if (m) {
InsertLast(msgHead, m);
m->nick = StrDuplicate(nick);
m->msg = StrDuplicate(msg);
}
}
void SendNickf(char *nick, const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendNick(nick, buffer);
}
/* --- Send ------------------------------------------------------- */
/* Send to client if possible, else to nick */
void Send(char *nick, char *msg)
{
snapshot;
if (msg) {
if (chat) {
if (client->status == CL_CONNECTED) {
/* Ship immediately to current client */
WriteSocket(client->socket, msg);
}
else {
SendNick(client->ident->nick, msg);
}
}
else if (nick) {
/* This is only /msg, don't flood */
SendNick(nick, msg);
}
}
}
void Sendf(char *nick, const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Send(nick, buffer);
}
/* --- SendMulti -------------------------------------------------- */
void SendMulti(char *nick, const char *format, ...)
{
char *buffer, *pointer;
size_t size = MAXLINE - 10; /* Preserve space for CR-LF and some extra */
va_list args;
snapshot;
va_start(args, format);
buffer = trio_vaprintf(format, args);
va_end(args);
if (buffer) {
if (nick) {
/* Preserve space for ":<botmatch> NOTICE <from> :" */
size -= (StrLength(botmatch) + StrLength(nick) + 20);
}
for (pointer = StrSplitMax(buffer, size); pointer;
pointer = StrSplitMax(NULL, size)) {
Send(nick, pointer);
}
free(buffer);
}
}
/* --- SendCtcp --------------------------------------------------- */
void ReplyCtcp(char *nick, char *msg)
{
char buffer[BIGBUFFER];
snapshot;
CtcpQuote(buffer, sizeof(buffer), msg);
WriteServer("NOTICE %s :\001%s\001", nick, buffer);
}
void ReplyCtcpf(char *nick, const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
ReplyCtcp(nick, buffer);
}
void SendCtcp(char *nick, char *msg)
{
char buffer[BIGBUFFER];
snapshot;
CtcpQuote(buffer, sizeof(buffer), msg);
WriteServer("PRIVMSG %s :\001%s\001", nick, buffer);
}
void SendCtcpf(char *nick, const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendCtcp(nick, buffer);
}
/* --- Multicast -------------------------------------------------- */
/* types: see *CAST in header file */
void MulticastLocal(int type, char *msg)
{
itemclient *p;
for (p = First(clientHead); p; p = Next(p)) {
if ((type&CHATCAST) && /* this is a CHAT message */
current &&
current->client && /* the current user has a client connected */
(current->client == p) && /* this client is the current user's */
!p->chatecho) /* this client has turned off echo */
continue; /* don't send anything to this client */
if ((p->status == CL_CONNECTED) && (p->flags & type)) {
WriteSocket(p->socket, msg);
}
}
}
void MulticastLocalf(int type, const char *format, ...)
{
char buffer[BIGGERBUFFER];
va_list args;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
MulticastLocal(type, buffer);
}
void Multicast(int type, char *msg)
{
MulticastLocal(type, msg);
SendLinkAll(IBCP_MULTICAST, "%d %s", type, msg);
}
void Multicastf(int type, const char *format, ...)
{
char buffer[BIGGERBUFFER];
va_list args;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Multicast(type, buffer);
}
/* --- FreeMessage ------------------------------------------------ */
void FreeMessage(void *v)
{
itemmsg *m;
snapshot;
m = (itemmsg *)v;
if (m) {
if (m->nick)
StrFree(m->nick);
if (m->msg)
StrFree(m->msg);
}
}
void FlushMessages(void)
{
FlushList(msgHead, FreeMessage);
}
/* --- MessageQueue ----------------------------------------------- */
/* Used to send delayed messages */
void MessageQueue(void)
{
static time_t lastsent = 0;
static int sentmsgs = 0;
itemmsg *m;
snapshot;
if (connected) {
if (kickQ && botop) {
if (!KickFromQueue(&lastsent))
kickQ = 0; /* cleared */
}
else {
AlertMode(ALERT_OFF); /* Switches off alert mode with timeout */
/*
* Send queued messages if present and we haven't sent too many too fast
*/
for (m = First(msgHead); m && (sentmsgs < 3); m = First(msgHead), sentmsgs++) {
WriteNick(m->nick, m->msg);
lastsent = now;
DeleteEntry(msgHead, m, FreeMessage);
}
}
if ((sentmsgs > 0) && ((lastsent + 2) <= now)) {
sentmsgs--;
}
}
}
/* --- MessageReaper ---------------------------------------------- */
void MessageReaper(char *target) /* Removes all messages to 'target' */
{
itemmsg *m, *next;
snapshot;
for (m = First(msgHead); m; m = next) {
next = Next(m);
if (StrEqual(m->nick, target))
DeleteEntry(msgHead, m, FreeMessage);
}
}
/* --- Execute ---------------------------------------------------- */
/* Remember to quote ALL user input to avoid cracking.
* Remember to redirect stderr to stdout by appending "2>&1"
*/
itemexec *Exec(char *nick, char *cmd)
{
itemexec *px;
FILE *f;
snapshot;
f = popen(cmd, "r");
if (f) {
px = NewEntry(itemexec);
if (px) {
InsertLast(execHead, px);
px->client = client;
if (nick)
px->nick = StrDuplicate(nick);
px->pipe = f;
px->socket = fileno(px->pipe);
FD_SET(px->socket, &rdset);
return px;
}
}
return NULL;
}
bool IsQualifier(char c)
{
snapshot;
switch (c) {
case '-': case '+': case ' ': case '#': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'h': case 'l': case 'L':
return TRUE;
}
return FALSE;
}
itemexec *Execute(char *nick, char *format, ...)
{
char buffer[BIGGERBUFFER];
char *pointer, *p;
bool wrong = FALSE;
va_list args;
snapshot;
if (execprotect) {
Send(nick, GetText(msg_execprotect_on));
return NULL;
}
/*
* Search string arguments for end-quotes.
* These checks are done to avoid cracking.
* Note: if user input isn't quoted they can
* access ';', '&', '|' and '^' special
* characters.
*/
va_start(args, format);
for (pointer = format; *pointer; pointer++) {
if ('%' == *pointer) {
for (pointer++; IsQualifier(*pointer); pointer++);
if ((char)0 == *pointer)
break;
switch (*pointer) {
/* Examine strings */
case 's':
p = va_arg(args, char *);
while (*p) {
switch (*p++) {
case '"':
case '`':
#if 0
case ';':
case '&':
case '|':
case '^':
#endif
wrong = TRUE;
break;
default:
break;
}
}
break;
/* Ignore the rest */
case 'e': case 'E':
case 'g': case 'G':
case 'f':
va_arg(args, double);
break;
case 'd': case 'i':
case 'o': case 'u':
case 'x': case 'X':
case 'c':
va_arg(args, int);
break;
case 'p':
va_arg(args, void *);
break;
default:
va_arg(args, void *);
break;
}
}
}
if (wrong) {
Send(nick, GetText(msg_cannot_do_that));
}
else {
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
return Exec(nick, buffer);
}
return NULL;
}
void FreeExec(void *v)
{
itemexec *px;
px = (itemexec *)v;
if (v) {
if (px->nick)
StrFree(px->nick);
if ((0 <= px->socket) && FD_ISSET(px->socket, &rdset))
FD_CLR(px->socket, &rdset);
if (px->pipe)
pclose(px->pipe);
}
}
/* --- GotExec ---------------------------------------------------- */
/* Ought to check if socket is still active */
void GotExec(itemexec *px)
{
char buffer[MAXLINE];
snapshot;
chat = (px->client != NULL);
if (fgets(buffer, sizeof(buffer), px->pipe)) {
if (StrTokenize(buffer, "\r\n") == buffer) {
client = px->client;
Send(px->nick, buffer);
}
}
else {
if (px->notify) {
/* This is a file transfer client, we should therefor set the END OF
TRANSFER bit in that struct to let it know */
itemclient *c;
for (c = First(clientHead); c; c = Next(c)) {
if (c->ident && StrEqual(c->ident->nick, px->nick) &&
(OUT_SEND == c->linksort)) { /* Get the send, nothing else */
c->fileflags |= CLF_FILECOMPLETE;
break;
}
}
}
client = px->client;
if (client) {
Send(NULL, GetText(msg_done));
}
else if (px->nick && !IsChannel(px->nick)) {
Send(px->nick, GetText(msg_done));
}
DeleteEntry(execHead, px, FreeExec);
}
}
/* --- SnoopCommand ----------------------------------------------- */
void SnoopCommand(char *from, char *cmd, char *param)
{
char buffer[BIGGERBUFFER];
char *who;
struct Command *command;
snapshot;
/* Skip if stealth user or no clients are attached */
if (First(clientHead) && current && ((current->user &&
!current->user->flags.stealth) || !current->user)) {
who = (from ? from : client->ident->nick);
if (cmd && *cmd) {
command = FindCommand(cmd);
/* Filter certain command from spylink */
if (command && command->hide) {
StrFormatMax(buffer, sizeof(buffer), GetDefaultText(msg_spy_hidden_command),
who, cmd, GetDefaultText(msg_snoop_prevented));
}
else {
StrFormatMax(buffer, sizeof(buffer), GetDefaultText(msg_spy_who_did_what),
who, cmd, *param ? " " : "", param);
}
Multicast(SPYCAST, buffer);
}
}
}
/* --- Say -------------------------------------------------------- */
void Say(char *msg)
{
snapshot;
WriteServer("PRIVMSG %s :%s", channel, msg);
lastpost = now;
}
void Sayf(const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Say(buffer);
}
/* --- Action ----------------------------------------------------- */
void Action(char *msg)
{
snapshot;
/* If we don't send this raw we can't use i.e control codes etc */
WriteServer("PRIVMSG %s :\001ACTION %s\001", channel, msg);
lastpost = now;
}
void Actionf(const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Action(buffer);
}
/* --- Mode ------------------------------------------------------- */
void Mode(const char *format, ...)
{
char buffer[BIGBUFFER];
va_list args;
snapshot;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
WriteServer("MODE %s %s", channel, buffer);
Logf(LOGDBUG, "sent MODE %s %s", channel, buffer);
}
/* --- Kick ------------------------------------------------------- */
time_t kicklast = 0;
int kickcount = 0;
void Kick(char *nick, char *msg)
{
itemguest *w;
snapshot;
if ((kicklast + 1) >= now)
kickcount++;
else
kickcount = 0;
w = FindNick(nick);
if (NULL == w)
return;
w->flags.kick = TRUE;
w->kicktime = now;
if (!botop || kickQ || (kickcount > 2)) {
kickQ++; /* One more in the pipe */
if (NULL == w->kickmsg) /* Only get the first message */
w->kickmsg = StrDuplicate(msg);
/*
* Stress situation, stop replying to CTCPs for a little while!
* We do this is we're not chanops too, since then we'll be able to
* kick this person as soon as we get opped! ;)
*/
CTCPignore();
Logf(LOGDBUG, "Enqueued KICK %s", nick);
}
else {
if (!w->flags.kicked) {
Logf(LOGDBUG, "Sent KICK %s to server", nick);
WriteServer("KICK %s %s :%s", channel, nick, (msg ? msg : nickname));
w->flags.kicked = TRUE;
}
kicklast = now;
}
}
void StickyKick(itemguest *w, char *msg)
{
snapshot;
AddKick(w->ident, nickname, msg, KICK_BOT);
if (!w->flags.kick)
Kick(w->ident->nick, msg);
#if 0
else
Logf(LOGDBUG, "Held back KICK %s (already issued)", w->ident->nick);
#endif
}
/* --- Invite ----------------------------------------------------- */
void Invite(char *who)
{
snapshot;
WriteServer("INVITE %s %s", who, channel);
}
/* --- Log -------------------------------------------------------- */
int lastlogday = -1;
int logdays = 10;
ulong activelog = -1;
static void RenameAndDelete(void)
{
char buffer[BIGBUFFER];
time_t then = now;
struct tm *t;
snapshot;
then -= (SECINDAY); /* 24 hours ago we were in yesterday land */
t = localtime(&then);
StrFormatMax(buffer, sizeof(buffer), "%s.%d%02d%02d",
logfile, 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday);
rename(logfile, buffer); /* Rename yesterday's logfile */
then -= (SECINDAY*logdays); /* Even further back */
t = localtime(&then); /* Get the date to delete */
StrFormatMax(buffer, sizeof(buffer), "%s.%d%02d%02d",
logfile, 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday);
remove(buffer); /* Remove the old one */
}
void LogInit(void)
{
struct stat stbuf;
struct tm *t;
snapshot;
t = localtime(&now);
lastlogday = t->tm_yday;
if (logfile[0]) {
if (-1 != stat(logfile, &stbuf)) {
t = localtime(&stbuf.st_mtime);
if (t->tm_yday != lastlogday) /* Backup logfile */
RenameAndDelete();
}
}
}
/* See enum in header */
char *logtypes[] = {
"***", "Join", "Part",
"Quit", "Nick", "Mode",
"Kick", ">>>", "#>>",
"-->", "Ctcp", "Client",
"Warn", "NSplit", "NJoin",
"NHeal", "DEBUG", "DBUG",
"INIT", "#",
"Kill", "Topic",
NULL
};
void Log(int type, char *buffer)
{
extern char servername[];
extern time_t uptime;
bool logfile_exists;
struct tm *t;
FILE *f;
t = localtime(&now);
#if defined(DBUG)
printf("%02d.%02d.%02d %-7s %s\n",
t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
#else /* !DBUG */
if ((activelog & (1 << type)) && (-1 != lastlogday) && logfile[0]) {
logfile_exists = FileExist(logfile);
if (lastlogday != t->tm_yday) {
lastlogday = t->tm_yday;
if (logfile_exists) {
RenameAndDelete(); /* Timestamp logfile */
t = localtime(&now); /* struct tm is static date that the
RenameAndDelete() ruined */
logfile_exists = FALSE;
}
}
f = fopen(logfile, "a");
if (f) {
if (!logfile_exists) {
fprintf(f,
"\n"
"--- Log for %02d.%02d.%d Server: %s Channel: %s\n"
"--- Nick: %s%s Version: %s Started: %s ago\n"
"\n",
t->tm_mday, t->tm_mon + 1, t->tm_year,
servername[0] ? servername : "Not connected",
(servername[0] && channel[0]) ? channel : "None joined",
botop ? "@" : "",
nickname[0] ? nickname : "No nick",
VERSIONMSG, TimeAgo(uptime));
}
fprintf(f, "%02d.%02d.%02d %-7s %s\n",
t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
fclose(f);
}
}
else if (-1 == lastlogday) {
fprintf(stderr, "%02d.%02d.%02d %-7s %s\n",
t->tm_hour, t->tm_min, t->tm_sec, logtypes[type], buffer);
}
#endif
}
void Logf(int type, const char *format, ...)
{
char buffer[BIGGERBUFFER];
va_list args;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
Log(type, buffer);
}
/* --- Snapshot --------------------------------------------------- */
#ifdef DEBUG
#define MAXSNAP 20
static int snapnum = MAXSNAP - 1;
static struct snapstructure {
char *file;
int line;
int hits;
} snap[MAXSNAP];
void MakeSnapshot(char *file, int line)
{
if ((snap[snapnum].line != line) ||
(snap[snapnum].file != file)) {
if (++snapnum >= MAXSNAP)
snapnum = 0;
snap[snapnum].file = file;
snap[snapnum].line = line;
snap[snapnum].hits = 1;
}
else
snap[snapnum].hits++;
}
void LogSnapshots(void)
{
char *buffer;
int fd, amount, index, i;
if (logfile[0]) {
fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
if (0 <= fd) {
for (amount = 0; (amount < MAXSNAP) && snap[i].file; amount++);
write(fd, "\n--- List of recent snapshots\n",
StrLength("\n--- List of recent snapshots\n"));
for (i = 1, index = snapnum; i <= amount; index--, i++) {
if (0 > index) {
index = MAXSNAP - 1;
}
buffer = trio_aprintf("#%-2d in %s line %d [%d]\n",
i,
snap[index].file,
snap[index].line,
snap[index].hits);
if (buffer) {
write(fd, buffer, StrLength(buffer));
free(buffer);
}
}
write(fd, "--- End of list\n\n",
StrLength("--- End of list\n\n"));
close(fd);
}
}
}
char *GetSnapFile(void)
{
return snap[snapnum].file;
}
int GetSnapLine(void)
{
return snap[snapnum].line;
}
#endif /* DEBUG */
/* --- Debug ------------------------------------------------------ */
void Debug(const char *format, ...)
{
char buffer[BIGGERBUFFER];
va_list args;
va_start(args, format);
trio_vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
#ifdef DEBUG
StrFormatAppendMax(buffer, sizeof(buffer), " (snapshot: %s line %d)",
GetSnapFile(), GetSnapLine());
#endif
Log(LOGDEBUG, buffer);
Multicastf(DEBUGCAST, "DEBUG: %s", buffer);
}
/* --- Quit ------------------------------------------------------- */
void Quit(char *from, char *reason)
{
snapshot;
/* Seen & Delete also handled by OnQuit, but how about restart/cleanup? */
SeenInsertAll(SEENQUITED, NULL, NULL);
DeleteGuests();
WriteServer("QUIT :%s", ((reason && *reason) ? reason : NOREASON));
DisconnectServ("QUIT %s", ((reason && *reason) ? reason : NOREASON));
restart = FALSE;
cleanup = TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1