irclogbot/irclogbot.c

245 lines
6.2 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include "libircclient/include/libircclient.h"
#include "configfile.h"
#include "list.h"
#include "logging.h"
#define DEFAULT_CONFIG "irclogbot.conf"
#define DEFAULT_LOGFILE "irclogbot.log"
static struct option opts[] = {
{"config", 1, 0, 'c'},
{"debug", 0, 0, 'd'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
struct irc_client {
struct list_head list;
irc_session_t *session;
const char *server;
const char *nickname;
const char *channel;
const char *channelkey;
struct in_addr addr;
};
static LIST_HEAD(client_list);
static irc_callbacks_t irc_cbs;
static int add_client(const char *parameter, void *privdata)
{
struct irc_client *entry = malloc(sizeof(struct irc_client));
if (entry == NULL) {
log_print(LOG_ERROR, "add_client(): out of memory");
return -1;
}
if (inet_pton(AF_INET, parameter, &entry->addr) <= 0) {
log_print(LOG_ERROR, "add_client(): inet_pton()");
free(entry);
return -1;
}
const char *defserver = config_get_string("global", "server", NULL);
entry->server = config_get_string(parameter, "server", defserver);
if (entry->server == NULL) {
log_print(LOG_ERROR, "add_client(): client %s: no server given", parameter);
free(entry);
return -1;
}
const char *defchannel = config_get_string("global", "channel", NULL);
entry->channel = config_get_string(parameter, "channel", defchannel);
if (entry->channel == NULL) {
log_print(LOG_ERROR, "add_client(): client %s: no channel given", parameter);
free(entry);
return -1;
}
const char *defchannelkey = config_get_string("global", "channelkey", NULL);
entry->channelkey = config_get_string(parameter, "channelkey", defchannelkey);
entry->nickname = config_get_string(parameter, "nickname", NULL);
if (entry->nickname == NULL) {
log_print(LOG_ERROR, "add_client(): client %s: no nickname given", parameter);
free(entry);
return -1;
}
entry->session = irc_create_session(&irc_cbs);
if (entry->session == NULL) {
log_print(LOG_ERROR, "add_client(): irc_create_session()");
free(entry);
return -1;
}
irc_set_ctx(entry->session, entry);
irc_option_set(entry->session, LIBIRC_OPTION_STRIPNICKS);
int ret = irc_connect(entry->session, entry->server, 6667, NULL, entry->nickname, NULL, NULL);
if (ret != 0) {
log_print(LOG_ERROR, "add_client(): irc_connect(): %s", irc_strerror(irc_errno(entry->session)));
irc_destroy_session(entry->session);
free(entry);
return -1;
}
/* HACK: irc_connect() sets errno */
errno = 0;
log_print(LOG_DEBUG, "added client %s (server: %s chan: %s nickname: %s)",
parameter, entry->server, entry->channel, entry->nickname);
list_add(&entry->list, &client_list);
return 0;
}
static void event_connect(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
{
struct irc_client *entry = (struct irc_client *) irc_get_ctx(session);
irc_cmd_join(session, entry->channel, entry->channelkey);
}
static void event_join(irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count)
{
struct irc_client *entry = (struct irc_client *) irc_get_ctx(session);
irc_cmd_user_mode(session, "+i");
char buf[32] = { "forwarding from " };
inet_ntop(AF_INET, &entry->addr, buf +16, 16);
irc_cmd_msg(session, params[0], buf);
}
static void event_numeric(irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count)
{
if (event < 400)
return;
log_print(LOG_INFO, "EVENT %d: %s: %s %s %s %s",
event, (origin ? origin : "unknown"), params[0],
count > 1 ? params[1] : "",
count > 2 ? params[2] : "",
count > 3 ? params[3] : "");
}
int main(int argc, char *argv[])
{
char *config = DEFAULT_CONFIG;
int code, arg = 0, debug = 0;
do {
code = getopt_long(argc, argv, "c:dh", opts, &arg);
switch (code) {
case 'c': /* config */
config = optarg;
break;
case 'd': /* debug */
debug = 1;
break;
case 'h': /* help */
printf("Usage: ctstat [options]\n"
"Options: \n"
" --config -c configfile use this configfile\n"
" --debug -d do not fork and log to stderr\n"
" --help -h this help\n"
"\n");
exit(0);
break;
case '?': /* error */
exit(-1);
break;
default: /* unknown / all options parsed */
break;
}
} while (code != -1);
/* parse config file */
if (config_parse(config))
exit(1);
int sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
log_print(LOG_ERROR, "socket()");
return -1;
}
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(514),
.sin_addr.s_addr = INADDR_ANY,
};
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
log_print(LOG_ERROR, "bind()");
return -1;
}
irc_cbs.event_connect = event_connect;
irc_cbs.event_join = event_join;
irc_cbs.event_numeric = event_numeric;
int cnt = config_get_strings("global", "client", add_client, NULL);
if (cnt == 0) {
log_print(LOG_ERROR, "no clients defined");
close(sock);
exit(1);
}
char buf[1500];
while (1) {
fd_set rd_fds, wr_fds;
FD_ZERO(&rd_fds);
FD_ZERO(&wr_fds);
FD_SET(sock, &rd_fds);
int maxfd = sock;
struct irc_client *entry;
list_for_each_entry(entry, &client_list, list) {
int ret = irc_add_select_descriptors(entry->session, &rd_fds, &wr_fds, &maxfd);
if (ret != 0)
log_print(LOG_WARN, "irc_add_select_descriptors(): %s", irc_strerror(irc_errno(entry->session)));
}
select(maxfd +1, &rd_fds, &wr_fds, NULL, NULL);
int len = 0;
if (FD_ISSET(sock, &rd_fds)) {
unsigned int i = sizeof(addr);
len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &i);
}
list_for_each_entry(entry, &client_list, list) {
if (len > 0 && (entry->addr.s_addr == addr.sin_addr.s_addr))
irc_cmd_msg(entry->session, entry->channel, buf);
int ret = irc_process_select_descriptors(entry->session, &rd_fds, &wr_fds);
if (ret != 0)
log_print(LOG_WARN, "irc_process_select_descriptors(): %s", irc_strerror(irc_errno(entry->session)));
}
}
return 0;
}