#include "nexus.h"
#include "signals.h"
#include "log.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#define DEFAULT_HOST "irc.fixme.fi"
#define DEFAULT_PORT "6667"
#define DEFAULT_PORT_SSL "6697"
/**
* Command-line option codes
*/
enum option_code {
OPT_HELP = 'h',
OPT_NETWORK = 'n',
OPT_CHANNEL = 'c',
OPT_MODULE = 'm',
OPT_CONFIG = 'C',
/** Options without short names */
_OPT_EXT_BEGIN = 0x00ff,
};
/**
* Command-line option definitions
*/
static struct option options[] = {
{"help", 0, NULL, OPT_HELP },
{"network", 1, NULL, OPT_NETWORK },
{"channel", 1, NULL, OPT_CHANNEL },
{"module", 1, NULL, OPT_MODULE },
{"config", 1, NULL, OPT_CONFIG },
{0, 0, 0, 0 },
};
/**
* Display --help output on stdout
*/
static void usage (const char *exe)
{
printf("Usage: %s [OPTIONS]\n", exe);
printf("\n");
printf(" --help / -h display this message\n");
printf(" --network / -n add an IRC network using '<name>:<hostname>[:<port>[:ssl]]' format\n");
printf(" --channel / -c add an IRC channel using '<network>:<channel>' format\n");
printf(" --module / -m add a module using '<name>:<path>' format\n");
printf(" --config / -C add a module configuration option using '<mod_name>:<name>[:<value>]' format\n");
}
/**
* Parse and apply a --network option
*/
static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err)
{
struct irc_net_info info = {
.network = NULL,
.hostname = NULL,
.service = DEFAULT_PORT,
.use_ssl = false,
.register_info = {
.nickname = "SpBotDev",
.username = "spbot-dev",
.realname = "SpBot (development version)"
}
};
// parse the required fields
if ((info.network = strsep(&opt, ":")) == NULL)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --network");
if ((info.hostname = strsep(&opt, ":")) == NULL)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <hostname> field for --network");
// parse the optional fields
if (opt)
info.service = strsep(&opt, ":");
// parse any remaining flags
while (opt) {
char *flag = strsep(&opt, ":");
if (strcmp(flag, "ssl"))
info.use_ssl = true;
else
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network");
}
// create the net
if (irc_client_add_net(nexus->client, NULL, &info, err))
return ERROR_CODE(err);
// ok
return SET_ERROR(err, SUCCESS);
}
/**
* Parse and apply a --channel option
*/
static err_t apply_channel (struct nexus *nexus, char *opt, struct error_info *err)
{
const char *network = NULL;
struct irc_net *net;
struct irc_chan_info info = {
.channel = NULL,
};
// parse the required fields
if ((network = strsep(&opt, ":")) == NULL)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <network> field for --channel");
if ((info.channel = strsep(&opt, ":")) == NULL)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <channel> field for --channel");
// trailing garbage?
if (opt)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
// look up the net
if ((net = irc_client_get_net(nexus->client, network)) == NULL)
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown network for --channel");
// add the channel
if (irc_net_add_chan(net, NULL, &info, err))
return ERROR_CODE(err);
// ok
return SUCCESS;
}
/**
* Parse arguments and apply them to the given nexus
*/
static err_t parse_args (struct nexus *nexus, int argc, char **argv, struct error_info *err)
{
int opt, option_index;
// parse options
while ((opt = getopt_long(argc, argv, "hn:c:m:C:", options, &option_index)) != -1) {
switch (opt) {
case OPT_HELP:
usage(argv[0]);
// XXX: return instead
exit(EXIT_SUCCESS);
case OPT_NETWORK:
if (apply_network(nexus, optarg, err))
return ERROR_CODE(err);
break;
case OPT_CHANNEL:
if (apply_channel(nexus, optarg, err))
return ERROR_CODE(err);
break;
case OPT_MODULE:
case OPT_CONFIG:
RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "option unimplemented");
case '?':
usage(argv[0]);
return SET_ERROR(err, ERR_CMD_OPT);
}
}
// ok
return SUCCESS;
}
static void on_sigint (evutil_socket_t sig, short what, void *arg)
{
struct nexus *ctx = arg;
(void) sig;
(void) what;
if (ctx->client && !ctx->client->quitting) {
log_info("Quitting...");
// quit it
irc_client_quit(ctx->client, "Goodbye, cruel world ;(");
} else {
log_error("Aborting");
// die
if (ctx->client) {
irc_client_destroy(ctx->client);
ctx->client = NULL;
}
// exit
event_base_loopexit(ctx->ev_base, NULL);
}
}
int main (int argc, char **argv)
{
struct signals *signals;
struct error_info err;
struct nexus _nexus, *nexus = &_nexus;
// zero nexus
memset(nexus, 0, sizeof(*nexus));
// initialize libevent
if ((nexus->ev_base = event_base_new()) == NULL)
FATAL("event_base_new");
// initialize signal handlers
if ((ERROR_CODE(&err) = signals_create(&signals, nexus->ev_base)))
FATAL("signals_create");
// add our signal handlers
if (
(ERROR_CODE(&err) = signals_add(signals, SIGPIPE, &signals_ignore, signals))
|| (ERROR_CODE(&err) = signals_add(signals, SIGINT, &on_sigint, nexus))
)
FATAL_ERROR(&err, "signals_add");
// initialize sock module
if (sock_init(nexus->ev_base, &err))
FATAL_ERROR(&err, "sock_init");
// modules
if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus)))
FATAL_ERROR(&err, "modules_create");
// the IRC client
if (irc_client_create(&nexus->client, &err))
FATAL_ERROR(&err, "irc_client_create");
// parse args
if (parse_args(nexus, argc, argv, &err))
FATAL_ERROR(&err, "parse_args");
// run event loop
if (event_base_dispatch(nexus->ev_base))
FATAL("event_base_dispatch");
// ok, no cleanup
return 0;
}