#include #include #include #include #include #include #include #include #include #include #include #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; }