mirror of https://github.com/nealey/irc-bot
Merge dispatch and irc-filter
This commit is contained in:
parent
962537e98f
commit
1e527f64bb
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
CFLAGS = -Wall -Werror
|
||||
TARGETS = dispatch irc-filter irc-esc
|
||||
TARGETS = bot
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
|
|
|
@ -0,0 +1,461 @@
|
|||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
#include "dispatch.h"
|
||||
|
||||
#define MAX_ARGS 50
|
||||
#define MAX_SUBPROCS 50
|
||||
#define TARGET_MAX 20
|
||||
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
char *handler = NULL;
|
||||
char *msgdir = NULL;
|
||||
struct timeval output_interval = {0};
|
||||
|
||||
void
|
||||
maybe_setenv(char *key, char *val)
|
||||
{
|
||||
if (val) {
|
||||
setenv(key, val, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
irc_filter(char *line)
|
||||
{
|
||||
char *parts[20] = {0};
|
||||
int nparts;
|
||||
char snick[20];
|
||||
char *cmd;
|
||||
char *text = NULL;
|
||||
char *prefix = NULL;
|
||||
char *sender = NULL;
|
||||
char *forum = NULL;
|
||||
int i;
|
||||
|
||||
/* Tokenize IRC line */
|
||||
nparts = 0;
|
||||
if (':' == *line) {
|
||||
prefix = line + 1;
|
||||
} else {
|
||||
parts[nparts++] = line;
|
||||
}
|
||||
while (*line) {
|
||||
if (' ' == *line) {
|
||||
*line++ = '\0';
|
||||
if (':' == *line) {
|
||||
text = line+1;
|
||||
break;
|
||||
} else {
|
||||
parts[nparts++] = line;
|
||||
}
|
||||
} else {
|
||||
line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip trailing carriage return */
|
||||
while (*line) line += 1;
|
||||
if ('\r' == *(line-1)) *(line-1) = '\0';
|
||||
|
||||
/* Set command, converting to upper case */
|
||||
cmd = parts[0];
|
||||
for (i = 0; cmd[i]; i += 1) {
|
||||
cmd[i] = toupper(cmd[i]);
|
||||
}
|
||||
|
||||
/* Extract prefix nickname */
|
||||
for (i = 0; prefix && (prefix[i] != '!'); i += 1) {
|
||||
if (i == sizeof(snick) - 1) {
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
snick[i] = prefix[i];
|
||||
}
|
||||
snick[i] = '\0';
|
||||
if (i) {
|
||||
sender = snick;
|
||||
}
|
||||
|
||||
/* Determine forum */
|
||||
if ((0 == strcmp(cmd, "PRIVMSG")) ||
|
||||
(0 == strcmp(cmd, "NOTICE"))) {
|
||||
/* :neale!user@127.0.0.1 PRIVMSG #hydra :foo */
|
||||
switch (parts[1][0]) {
|
||||
case '#':
|
||||
case '&':
|
||||
case '+':
|
||||
case '!':
|
||||
forum = parts[1];
|
||||
break;
|
||||
default:
|
||||
forum = snick;
|
||||
break;
|
||||
}
|
||||
} else if ((0 == strcmp(cmd, "PART")) ||
|
||||
(0 == strcmp(cmd, "MODE")) ||
|
||||
(0 == strcmp(cmd, "TOPIC")) ||
|
||||
(0 == strcmp(cmd, "KICK"))) {
|
||||
forum = parts[1];
|
||||
} else if (0 == strcmp(cmd, "JOIN")) {
|
||||
if (0 == nparts) {
|
||||
forum = text;
|
||||
text = NULL;
|
||||
} else {
|
||||
forum = parts[1];
|
||||
}
|
||||
} else if (0 == strcmp(cmd, "INVITE")) {
|
||||
forum = text?text:parts[2];
|
||||
text = NULL;
|
||||
} else if (0 == strcmp(cmd, "NICK")) {
|
||||
sender = parts[1];
|
||||
forum = sender;
|
||||
} else if (0 == strcmp(cmd, "PING")) {
|
||||
printf("PONG :%s\r\n", text);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
{
|
||||
int _argc;
|
||||
char *_argv[MAX_ARGS + 1];
|
||||
|
||||
maybe_setenv("handler", handler);
|
||||
maybe_setenv("prefix", prefix);
|
||||
maybe_setenv("command", cmd);
|
||||
maybe_setenv("sender", sender);
|
||||
maybe_setenv("forum", forum);
|
||||
maybe_setenv("text", text);
|
||||
|
||||
_argc = 0;
|
||||
_argv[_argc++] = handler;
|
||||
for (i = 1; (i < nparts) && (_argc < MAX_ARGS); i += 1) {
|
||||
_argv[_argc++] = parts[i];
|
||||
}
|
||||
_argv[_argc] = NULL;
|
||||
|
||||
execvp(handler, _argv);
|
||||
perror(handler);
|
||||
}
|
||||
}
|
||||
|
||||
struct subproc {
|
||||
int fd; /* File descriptor */
|
||||
char buf[4000]; /* Read buffer */
|
||||
size_t buflen; /* Buffer length */
|
||||
};
|
||||
|
||||
struct subproc subprocs[MAX_SUBPROCS] = { {0} };
|
||||
|
||||
void
|
||||
dispatch(const char *buf, size_t buflen)
|
||||
{
|
||||
int subout[2];
|
||||
struct subproc *s = NULL;
|
||||
int i;
|
||||
char text[512];
|
||||
|
||||
if (buflen > sizeof(text)) {
|
||||
fprintf(stderr, "Ignoring message: too long (%u bytes)\n", (unsigned int) buflen);
|
||||
return;
|
||||
}
|
||||
memcpy(text, buf, buflen - 1); /* omit newline */
|
||||
text[buflen - 1] = '\0';
|
||||
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (0 == subprocs[i].fd) {
|
||||
s = &subprocs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
fprintf(stderr, "Ignoring message: too many subprocesses\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (-1 == pipe(subout)) {
|
||||
perror("pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == fork()) {
|
||||
/*
|
||||
* Child
|
||||
*/
|
||||
int null;
|
||||
|
||||
if ((-1 == (null = open("/dev/null", O_RDONLY))) ||
|
||||
(-1 == dup2(null, 0)) ||
|
||||
(-1 == dup2(subout[1], 1))) {
|
||||
perror("fd setup");
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll be a good citizen and only close file descriptors we opened.
|
||||
*/
|
||||
close(null);
|
||||
close(subout[0]);
|
||||
close(subout[1]);
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd) {
|
||||
close(subprocs[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
irc_filter(text);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
s->fd = subout[0];
|
||||
close(subout[1]);
|
||||
}
|
||||
|
||||
void
|
||||
delay_output()
|
||||
{
|
||||
struct timeval now;
|
||||
struct timeval diff;
|
||||
static struct timeval output_last = { 0 };
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&now, &output_last, &diff);
|
||||
if (timercmp(&diff, &output_interval, <)) {
|
||||
struct timeval delay;
|
||||
struct timespec ts;
|
||||
int ret;
|
||||
|
||||
timersub(&output_interval, &diff, &delay);
|
||||
|
||||
ts.tv_sec = (time_t) delay.tv_sec;
|
||||
ts.tv_nsec = (long) (delay.tv_usec * 1000);
|
||||
do {
|
||||
ret = nanosleep(&ts, &ts);
|
||||
} while ((-1 == ret) && (EINTR == errno));
|
||||
gettimeofday(&output_last, NULL);
|
||||
} else {
|
||||
output_last = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Writes all of buf to stdout, possibly blocking. */
|
||||
void
|
||||
output(const char *buf, size_t count)
|
||||
{
|
||||
if (timerisset(&output_interval)) {
|
||||
delay_output();
|
||||
}
|
||||
|
||||
while (count) {
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = write(1, buf, count);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
if (-1 == len) {
|
||||
perror("stdout");
|
||||
exit(EX_IOERR);
|
||||
}
|
||||
count -= len;
|
||||
buf += len;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
call_with_lines(char *buf, size_t * len, void (*func) (const char *, size_t))
|
||||
{
|
||||
char *b = buf;
|
||||
char *p;
|
||||
size_t l = *len;
|
||||
|
||||
while ((p = memchr(b, '\n', l))) {
|
||||
size_t n = p - b + 1;
|
||||
|
||||
func(b, n);
|
||||
l -= n;
|
||||
b += n;
|
||||
}
|
||||
memmove(buf, b, l);
|
||||
*len = l;
|
||||
}
|
||||
|
||||
char inbuf[8000];
|
||||
size_t inbuflen = 0;
|
||||
|
||||
void
|
||||
handle_input()
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = read(0, inbuf + inbuflen, sizeof(inbuf) - inbuflen);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
if (0 == len) {
|
||||
exit(0);
|
||||
}
|
||||
inbuflen += len;
|
||||
call_with_lines(inbuf, &inbuflen, dispatch);
|
||||
}
|
||||
|
||||
void
|
||||
handle_subproc(struct subproc *s)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = read(s->fd, s->buf + s->buflen, sizeof(s->buf) - s->buflen);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
|
||||
if (-1 == len) {
|
||||
perror("subprocess read error");
|
||||
} else {
|
||||
s->buflen += len;
|
||||
call_with_lines(s->buf, &s->buflen, output);
|
||||
}
|
||||
|
||||
if (sizeof(s->buf) == s->buflen) {
|
||||
fprintf(stderr, "subprocess buffer full, killing and discarding buffer.\n");
|
||||
len = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recycle this subproc unless something was read
|
||||
*/
|
||||
if (0 >= len) {
|
||||
if (s->buflen) {
|
||||
fprintf(stderr, "warning: discarding %u characters from subprocess buffer\n", (unsigned int) s->buflen);
|
||||
}
|
||||
close(s->fd);
|
||||
s->fd = 0;
|
||||
s->buflen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
loop()
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int nfds = 0;
|
||||
fd_set rfds;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(0, &rfds);
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd) {
|
||||
FD_SET(subprocs[i].fd, &rfds);
|
||||
nfds = max(nfds, subprocs[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
ret = select(nfds + 1, &rfds, NULL, NULL, NULL);
|
||||
} while ((-1 == ret) && (EINTR == errno));
|
||||
if (-1 == ret) {
|
||||
perror("select");
|
||||
exit(EX_IOERR);
|
||||
}
|
||||
|
||||
if (FD_ISSET(0, &rfds)) {
|
||||
handle_input();
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd && FD_ISSET(subprocs[i].fd, &rfds)) {
|
||||
handle_subproc(&subprocs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sigchld(int signum)
|
||||
{
|
||||
while (0 < waitpid(-1, NULL, WNOHANG));
|
||||
}
|
||||
|
||||
void
|
||||
usage(char *self)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] HANDLER\n", self);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "-h Display help.\n");
|
||||
fprintf(stderr, "-d DIR Also dispatch messages from DIR, one per file.\n");
|
||||
fprintf(stderr, "-i INTERVAL Wait at least INTERVAL microseconds between\n");
|
||||
fprintf(stderr, " sending each line.\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
/*
|
||||
* Parse command line
|
||||
*/
|
||||
while (!handler) {
|
||||
switch (getopt(argc, argv, "hd:i:")) {
|
||||
case -1:
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "error: must specify event handler.\n");
|
||||
usage(argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
handler = argv[optind];
|
||||
break;
|
||||
case 'd':
|
||||
msgdir = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
{
|
||||
char *end;
|
||||
long long int interval;
|
||||
|
||||
interval = strtoll(optarg, &end, 10);
|
||||
if (*end) {
|
||||
fprintf(stderr, "error: not an integer number: %s\n", optarg);
|
||||
return EX_USAGE;
|
||||
}
|
||||
output_interval.tv_sec = interval / 1000000;
|
||||
output_interval.tv_usec = interval % 1000000;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown option.\n");
|
||||
usage(argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tcpclient uses fds 6 and 7. If these aren't open, we keep the
|
||||
* original fds 0 and 1.
|
||||
*/
|
||||
if (-1 != dup2(6, 0)) {
|
||||
close(6);
|
||||
}
|
||||
if (-1 != dup2(7, 1)) {
|
||||
close(7);
|
||||
}
|
||||
|
||||
signal(SIGCHLD, sigchld);
|
||||
|
||||
while (1) {
|
||||
loop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
349
dispatch.c
349
dispatch.c
|
@ -1,349 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <sysexits.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "dump.h"
|
||||
|
||||
#define MAX_ARGS 50
|
||||
#define MAX_SUBPROCS 50
|
||||
#define TARGET_MAX 20
|
||||
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
struct subproc {
|
||||
int fd; /* File descriptor */
|
||||
char buf[4000]; /* Read buffer */
|
||||
size_t buflen; /* Buffer length */
|
||||
};
|
||||
|
||||
struct subproc subprocs[MAX_SUBPROCS] = {{0}};
|
||||
|
||||
/* Things set by argv parser */
|
||||
char *handler = NULL;
|
||||
char **handler_args;
|
||||
struct timeval output_interval = {0};
|
||||
struct timeval output_last = {0};
|
||||
int fifoin = -1;
|
||||
int fifoout = -1;
|
||||
|
||||
void
|
||||
dispatch(const char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
int subout[2];
|
||||
struct subproc *s = NULL;
|
||||
int i;
|
||||
char text[512];
|
||||
|
||||
if (buflen > sizeof(text)) {
|
||||
fprintf(stderr, "Ignoring message: too long (%u bytes)\n", (unsigned int)buflen);
|
||||
return;
|
||||
}
|
||||
memcpy(text, buf, buflen-1); /* omit newline */
|
||||
text[buflen-1] = '\0';
|
||||
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (0 == subprocs[i].fd) {
|
||||
s = &subprocs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! s) {
|
||||
fprintf(stderr, "Ignoring message: too many subprocesses\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (-1 == pipe(subout)) {
|
||||
perror("pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == fork()) {
|
||||
/* Child */
|
||||
char *argv[MAX_ARGS + 5];
|
||||
int null;
|
||||
int i;
|
||||
|
||||
if ((-1 == (null = open("/dev/null", O_RDONLY))) ||
|
||||
(-1 == dup2(null, 0)) ||
|
||||
(-1 == dup2(subout[1], 1))) {
|
||||
perror("fd setup");
|
||||
exit(EX_OSERR);
|
||||
}
|
||||
|
||||
/* We'll be good citizens about this and only close file descriptors
|
||||
we opened. */
|
||||
close(fifoout);
|
||||
close(null);
|
||||
close(subout[0]);
|
||||
close(subout[1]);
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd) {
|
||||
close(subprocs[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
argv[i++] = handler;
|
||||
for (; handler_args[i-1]; i += 1) {
|
||||
argv[i] = handler_args[i-1];
|
||||
}
|
||||
argv[i++] = text;
|
||||
argv[i] = NULL;
|
||||
|
||||
execvp(handler, argv);
|
||||
perror("exec");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
s->fd = subout[0];
|
||||
close(subout[1]);
|
||||
}
|
||||
|
||||
void
|
||||
delay_output()
|
||||
{
|
||||
struct timeval now, diff;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&now, &output_last, &diff);
|
||||
if (timercmp(&diff, &output_interval, <)) {
|
||||
struct timeval delay;
|
||||
struct timespec ts;
|
||||
int ret;
|
||||
|
||||
timersub(&output_interval, &diff, &delay);
|
||||
|
||||
ts.tv_sec = (time_t)delay.tv_sec;
|
||||
ts.tv_nsec = (long)(delay.tv_usec * 1000);
|
||||
do {
|
||||
ret = nanosleep(&ts, &ts);
|
||||
} while ((-1 == ret) && (EINTR == errno));
|
||||
gettimeofday(&output_last, NULL);
|
||||
} else {
|
||||
output_last = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Writes all of buf to stdout, possibly blocking. */
|
||||
void
|
||||
output(const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (timerisset(&output_interval)) {
|
||||
delay_output();
|
||||
}
|
||||
|
||||
while (count) {
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = write(1, buf, count);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
if (-1 == len) {
|
||||
perror("stdout");
|
||||
exit(EX_IOERR);
|
||||
}
|
||||
count -= len;
|
||||
buf += len;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
call_with_lines(char *buf,
|
||||
size_t *len,
|
||||
void (*func)(const char *, size_t))
|
||||
{
|
||||
char *b = buf;
|
||||
char *p;
|
||||
size_t l = *len;
|
||||
|
||||
while ((p = memchr(b, '\n', l))) {
|
||||
size_t n = p - b + 1;
|
||||
|
||||
func(b, n);
|
||||
l -= n;
|
||||
b += n;
|
||||
}
|
||||
memmove(buf, b, l);
|
||||
*len = l;
|
||||
}
|
||||
|
||||
char inbuf[8000];
|
||||
size_t inbuflen = 0;
|
||||
|
||||
void
|
||||
handle_input()
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = read(0, inbuf + inbuflen, sizeof(inbuf) - inbuflen);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
if (0 == len) {
|
||||
exit(0);
|
||||
}
|
||||
inbuflen += len;
|
||||
call_with_lines(inbuf, &inbuflen, dispatch);
|
||||
}
|
||||
|
||||
void
|
||||
handle_subproc(struct subproc *s)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
do {
|
||||
len = read(s->fd, s->buf + s->buflen, sizeof(s->buf) - s->buflen);
|
||||
} while ((-1 == len) && (EINTR == errno));
|
||||
if (-1 == len) {
|
||||
perror("subprocess read error");
|
||||
} else {
|
||||
s->buflen += len;
|
||||
call_with_lines(s->buf, &s->buflen, output);
|
||||
}
|
||||
|
||||
if (sizeof(s->buf) == s->buflen) {
|
||||
fprintf(stderr, "subprocess buffer full, killing and discarding buffer.\n");
|
||||
len = 0;
|
||||
}
|
||||
|
||||
/* Recycle this subproc unless something was read */
|
||||
if (0 >= len) {
|
||||
if (s->buflen) {
|
||||
fprintf(stderr, "warning: discarding %u characters from subprocess buffer\n",
|
||||
(unsigned int)s->buflen);
|
||||
}
|
||||
close(s->fd);
|
||||
s->fd = 0;
|
||||
s->buflen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
loop()
|
||||
{
|
||||
int i, ret;
|
||||
int nfds = 0;
|
||||
fd_set rfds;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(0, &rfds);
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd) {
|
||||
FD_SET(subprocs[i].fd, &rfds);
|
||||
nfds = max(nfds, subprocs[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
ret = select(nfds+1, &rfds, NULL, NULL, NULL);
|
||||
} while ((-1 == ret) && (EINTR == errno));
|
||||
if (-1 == ret) {
|
||||
perror("select");
|
||||
exit(EX_IOERR);
|
||||
}
|
||||
|
||||
if (FD_ISSET(0, &rfds)) {
|
||||
handle_input();
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SUBPROCS; i += 1) {
|
||||
if (subprocs[i].fd && FD_ISSET(subprocs[i].fd, &rfds)) {
|
||||
handle_subproc(&subprocs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sigchld(int signum)
|
||||
{
|
||||
while (0 < waitpid(-1, NULL, WNOHANG));
|
||||
}
|
||||
|
||||
void
|
||||
usage(char *self)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] handler [ARGS ...]\n", self);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "-f FIFO Also dispatch messages from FIFO.\n");
|
||||
fprintf(stderr, "-i INTERVAL Wait at least INTERVAL microseconds between\n");
|
||||
fprintf(stderr, " sending each line.\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
/* Parse command line */
|
||||
while (! handler) {
|
||||
switch (getopt(argc, argv, "hf:i:")) {
|
||||
case -1:
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "error: must specify handler script.\n");
|
||||
usage(argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
if (argc - optind - 10 > MAX_ARGS) {
|
||||
fprintf(stderr, "error: too many arguments to helper.\n");
|
||||
return EX_USAGE;
|
||||
}
|
||||
handler = argv[optind];
|
||||
handler_args = argv + (optind + 1);
|
||||
break;
|
||||
case 'f':
|
||||
if ((-1 == (fifoin = open(optarg, O_RDONLY | O_NONBLOCK))) ||
|
||||
(-1 == (fifoout = open(optarg, O_WRONLY)))) {
|
||||
perror("open fifo");
|
||||
return EX_IOERR;
|
||||
}
|
||||
subprocs[0].fd = fifoin;
|
||||
break;
|
||||
case 'i':
|
||||
{
|
||||
char *end;
|
||||
long long int interval;
|
||||
|
||||
interval = strtoll(optarg, &end, 10);
|
||||
if (*end) {
|
||||
fprintf(stderr, "error: not an integer number: %s\n", optarg);
|
||||
return EX_USAGE;
|
||||
}
|
||||
output_interval.tv_sec = interval / 1000000;
|
||||
output_interval.tv_usec = interval % 1000000;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown option.\n");
|
||||
usage(argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/* tcpclient uses fds 6 and 7. If these aren't open, we keep the
|
||||
original fds 0 and 1. */
|
||||
if (-1 != dup2(6, 0)) close(6);
|
||||
if (-1 != dup2(7, 1)) close(7);
|
||||
|
||||
signal(SIGCHLD, sigchld);
|
||||
|
||||
while (1) {
|
||||
loop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
154
irc-filter.c
154
irc-filter.c
|
@ -1,154 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include "dump.h"
|
||||
|
||||
#define MAX_ARGS 50
|
||||
#define MAX_OUTARGS 60
|
||||
#define MAX_PARTS 20
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *parts[20] = {0};
|
||||
int nparts;
|
||||
char snick[20];
|
||||
char *cmd;
|
||||
char *text = NULL;
|
||||
char *prefix = NULL;
|
||||
char *sender = NULL;
|
||||
char *forum = NULL;
|
||||
int i;
|
||||
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s HANDLER [ARGV ...] LINE\n", argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Parses LINE (an IRC message) into:\n");
|
||||
fprintf(stderr, " PREFIX Prefix part of message\n");
|
||||
fprintf(stderr, " COMMAND IRC command\n");
|
||||
fprintf(stderr, " SENDER Nickname of message's sender\n");
|
||||
fprintf(stderr, " FORUM Forum of message\n");
|
||||
fprintf(stderr, " TEXT Text part of message\n");
|
||||
fprintf(stderr, " ARGS... Arguments of message\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "After parsing, exec()s\n");
|
||||
fprintf(stderr, " HANDLER ARGV... PREFIX COMMAND SENDER FORUM TEXT ARGS...\n");
|
||||
return EX_USAGE;
|
||||
} else if (argc > MAX_ARGS) {
|
||||
fprintf(stderr, "%s: too many arguments\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
/* Tokenize IRC line */
|
||||
{
|
||||
char *line = argv[argc-1];
|
||||
|
||||
nparts = 0;
|
||||
if (':' == *line) {
|
||||
prefix = line + 1;
|
||||
} else {
|
||||
parts[nparts++] = line;
|
||||
}
|
||||
while (*line) {
|
||||
if (' ' == *line) {
|
||||
*line++ = '\0';
|
||||
if (':' == *line) {
|
||||
text = line+1;
|
||||
break;
|
||||
} else {
|
||||
parts[nparts++] = line;
|
||||
}
|
||||
} else {
|
||||
line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip trailing carriage return */
|
||||
while (*line) line += 1;
|
||||
if ('\r' == *(line-1)) *(line-1) = '\0';
|
||||
}
|
||||
|
||||
/* Set command, converting to upper case */
|
||||
cmd = parts[0];
|
||||
for (i = 0; cmd[i]; i += 1) {
|
||||
cmd[i] = toupper(cmd[i]);
|
||||
}
|
||||
|
||||
/* Extract prefix nickname */
|
||||
for (i = 0; prefix && (prefix[i] != '!'); i += 1) {
|
||||
if (i == sizeof(snick) - 1) {
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
snick[i] = prefix[i];
|
||||
}
|
||||
snick[i] = '\0';
|
||||
if (i) {
|
||||
sender = snick;
|
||||
}
|
||||
|
||||
/* Determine forum */
|
||||
if ((0 == strcmp(cmd, "PRIVMSG")) ||
|
||||
(0 == strcmp(cmd, "NOTICE"))) {
|
||||
/* :neale!user@127.0.0.1 PRIVMSG #hydra :foo */
|
||||
switch (parts[1][0]) {
|
||||
case '#':
|
||||
case '&':
|
||||
case '+':
|
||||
case '!':
|
||||
forum = parts[1];
|
||||
break;
|
||||
default:
|
||||
forum = snick;
|
||||
break;
|
||||
}
|
||||
} else if ((0 == strcmp(cmd, "PART")) ||
|
||||
(0 == strcmp(cmd, "MODE")) ||
|
||||
(0 == strcmp(cmd, "TOPIC")) ||
|
||||
(0 == strcmp(cmd, "KICK"))) {
|
||||
forum = parts[1];
|
||||
} else if (0 == strcmp(cmd, "JOIN")) {
|
||||
if (0 == nparts) {
|
||||
forum = text;
|
||||
text = NULL;
|
||||
} else {
|
||||
forum = parts[1];
|
||||
}
|
||||
} else if (0 == strcmp(cmd, "INVITE")) {
|
||||
forum = text?text:parts[2];
|
||||
text = NULL;
|
||||
} else if (0 == strcmp(cmd, "NICK")) {
|
||||
sender = parts[1];
|
||||
forum = sender;
|
||||
} else if (0 == strcmp(cmd, "PING")) {
|
||||
printf("PONG :%s\r\n", text);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
{
|
||||
int _argc;
|
||||
char *_argv[MAX_OUTARGS + 1];
|
||||
|
||||
_argc = 0;
|
||||
for (i = 1; i < argc-1; i += 1) {
|
||||
_argv[_argc++] = argv[i];
|
||||
}
|
||||
_argv[_argc++] = prefix?prefix:"";
|
||||
_argv[_argc++] = cmd;
|
||||
_argv[_argc++] = sender?sender:"";
|
||||
_argv[_argc++] = forum?forum:"";
|
||||
_argv[_argc++] = text?text:"";
|
||||
for (i = 1; (i < nparts) && (_argc < MAX_OUTARGS); i += 1) {
|
||||
_argv[_argc++] = parts[i];
|
||||
}
|
||||
_argv[_argc] = NULL;
|
||||
|
||||
execvp(_argv[0], _argv);
|
||||
perror(_argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue