add a simple irc_log module (with evsql code) that joins a channel and log_info's PRIVMSGs
--- a/Makefile Sun Mar 01 02:02:48 2009 +0200
+++ b/Makefile Sun Mar 08 17:17:37 2009 +0200
@@ -24,6 +24,10 @@
GNUTLS_CFLAGS = $(shell pkg-config ${GNUTLS_NAME} --cflags)
GNUTLS_LDFLAGS = $(shell pkg-config ${GNUTLS_NAME} --libs)
+# evsql stuff
+EVSQL_CFLAGS =
+EVSQL_LDFLAGS = -levsql -lpq
+
BIN_NAMES = nexus
BIN_PATHS = $(addprefix bin/,$(BIN_NAMES))
@@ -35,6 +39,7 @@
SOCK_GNUTLS_OBJS = obj/sock_gnutls.o
LINEPROTO_OBJS = obj/line_proto.o
IRC_OBJS = obj/irc_line.o obj/irc_conn.o
+IRC_LOG_OBJS = obj/irc_log.o
# XXX: not yet there
#CORE_OBJS = obj/lib/log.o obj/lib/signals.o
@@ -43,11 +48,11 @@
all: ${BIN_PATHS}
# binaries
-bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${LINEPROTO_OBJS} ${IRC_OBJS}
+bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${LINEPROTO_OBJS} ${IRC_OBJS} ${IRC_LOG_OBJS}
# computed
-CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS}
-LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS}
+CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS} ${EVSQL_CFLAGS}
+LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS} ${EVSQL_LDFLAGS}
# XXX: is this valid?
CPPFLAGS = ${CFLAGS}
--- a/src/error.h Sun Mar 01 02:02:48 2009 +0200
+++ b/src/error.h Sun Mar 08 17:17:37 2009 +0200
@@ -68,6 +68,9 @@
_ERROR_CODE( ERR_EVENT_NEW, 0x010201, NONE ),
_ERROR_CODE( ERR_EVENT_ADD, 0x010202, NONE ),
+ /* Evsql errors */
+ _ERROR_CODE( ERR_EVSQL_NEW_PQ, 0x010301, NONE ),
+
/* irc_line errors */
_ERROR_CODE( ERR_LINE_TOO_LONG, 0x100101, NONE ),
_ERROR_CODE( ERR_LINE_INVALID_TOKEN, 0x100102, NONE ),
--- a/src/irc_cmd.h Sun Mar 01 02:02:48 2009 +0200
+++ b/src/irc_cmd.h Sun Mar 08 17:17:37 2009 +0200
@@ -1,6 +1,41 @@
#ifndef IRC_CMD_H
#define IRC_CMD_H
+/**
+ * @file
+ *
+ * Flexible command handlers callback lists for use with irc_lines
+ */
+
+#include "irc_conn.h"
+#include "irc_line.h"
+#include <sys/queue.h>
+
+/**
+ * Single command -> handler mapping for lookup
+ */
+struct irc_cmd_handler {
+ /** The command name to match */
+ const char *command;
+
+ /** The handler function */
+ void (*func) (struct irc_conn *conn, const struct irc_line *line, void *arg);
+};
+
+/**
+ * List item for a chain of irc_cmd_handler entries, with the context pointer
+ */
+struct irc_cmd_chain {
+ /** The list of handler lookup entries */
+ struct irc_cmd_handler *handlers;
+
+ /** Opaque context arg */
+ void *arg;
+
+ /** Linked list */
+ STAILQ_ENTRY(irc_cmd_chain) node;
+};
+
/*
* IRC command numerics
*/
--- a/src/irc_conn.c Sun Mar 01 02:02:48 2009 +0200
+++ b/src/irc_conn.c Sun Mar 08 17:17:37 2009 +0200
@@ -9,7 +9,7 @@
/*
* "Welcome to the Internet Relay Network <nick>!<user>@<host>"
*/
-static void on_RPL_WELCOME (struct irc_conn *conn, const struct irc_line *line)
+static void on_RPL_WELCOME (struct irc_conn *conn, const struct irc_line *line, void *arg)
{
// update state
conn->registered = true;
@@ -22,7 +22,7 @@
*
* Send a 'PONG <server1>` reply right away.
*/
-static void on_PING (struct irc_conn *conn, const struct irc_line *line)
+static void on_PING (struct irc_conn *conn, const struct irc_line *line, void *arg)
{
// just reply
irc_conn_PONG(conn, line->args[0]);
@@ -31,23 +31,20 @@
/*
* Our command handlers
*/
-struct irc_cmd_handler {
- /* The command name */
- const char *command;
-
- /* The handler function */
- void (*func) (struct irc_conn *conn, const struct irc_line *line);
-
-} _cmd_handlers[] = {
+struct irc_cmd_handler _cmd_handlers[] = {
{ IRC_RPL_WELCOME, on_RPL_WELCOME },
{ "PING", on_PING },
{ NULL, NULL, },
};
+/*
+ * Incoming line handler
+ */
void irc_conn_on_line (char *line_buf, void *arg)
{
struct irc_conn *conn = arg;
struct irc_line line;
+ struct irc_cmd_chain *chain;
struct irc_cmd_handler *handler;
int err;
@@ -56,18 +53,22 @@
// parse
if ((err = irc_line_parse(&line, line_buf))) {
- printf("!!! Invalid line: %s: %s\n", line_buf, error_name(err));
-
+ log_warn("invalid line: %s: %s\n", line_buf, error_name(err));
return;
}
+
+ // run each handler chain
+ STAILQ_FOREACH(chain, &conn->handlers, node) {
+ // look up appropriate handler
+ for (handler = chain->handlers; handler->command; handler++) {
+ // the command is alpha-only, so normal case-insensitive cmp is fine
+ if (strcasecmp(handler->command, line.command) == 0) {
+ // invoke the func
+ handler->func(conn, &line, chain->arg);
- // look up appropriate handler
- for (handler = _cmd_handlers; handler->command; handler++) {
- // the command is alpha-only, so normal case-insensitive cmp is fine
- if (strcasecmp(handler->command, line.command) == 0) {
- // invoke the func
- handler->func(conn, &line);
- break;
+ // ...only one per chain
+ break;
+ }
}
}
}
@@ -80,6 +81,13 @@
if ((conn = calloc(1, sizeof(struct irc_conn))) == NULL)
return SET_ERROR(err, ERR_CALLOC);
+ // initialize command handlers
+ STAILQ_INIT(&conn->handlers);
+
+ // add the core handlers
+ if ((ERROR_CODE(err) = irc_conn_register_handler_chain(conn, _cmd_handlers, NULL)))
+ return ERROR_CODE(err);
+
// create the line_proto, with our on_line handler
if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &irc_conn_on_line, conn, err))
return ERROR_CODE(err);
@@ -97,6 +105,25 @@
return SUCCESS;
}
+err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
+{
+ struct irc_cmd_chain *item;
+
+ // allocate the chain item
+ if ((item = calloc(1, sizeof(*item))) == NULL)
+ return ERR_CALLOC;
+
+ // store
+ item->handlers = handlers;
+ item->arg = arg;
+
+ // append
+ STAILQ_INSERT_TAIL(&conn->handlers, item, node);
+
+ // ok
+ return SUCCESS;
+}
+
err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line)
{
char line_buf[IRC_LINE_MAX + 2];
@@ -148,3 +175,13 @@
return irc_conn_send(conn, &line);
}
+err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel)
+{
+ // JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"
+ struct irc_line line = {
+ NULL, "JOIN", { channel, NULL }
+ };
+
+ return irc_conn_send(conn, &line);
+}
+
--- a/src/irc_conn.h Sun Mar 01 02:02:48 2009 +0200
+++ b/src/irc_conn.h Sun Mar 08 17:17:37 2009 +0200
@@ -1,18 +1,25 @@
#ifndef IRC_CONN_H
#define IRC_CONN_H
-/*
- * Per-connection IRC setup and state/protocol handling.
+/**
+ * @file
+ *
+ * Support for connections to IRC servers, a rather fundamental thing. This holds the line_proto to handle the network
+ * communications, and then takes care of sending/receiving commands, as well as updating some core state like
+ * current nickname.
*/
+struct irc_conn;
+
+#include "error.h"
#include "sock.h"
#include "line_proto.h"
#include "irc_line.h"
-#include "error.h"
+#include "irc_cmd.h"
#include <stdbool.h>
/*
- * A connection to an IRC server.
+ * Connection state
*/
struct irc_conn {
/* We are a line-based protocol */
@@ -20,6 +27,9 @@
/* Registered (as in, we have a working nickname)? */
bool registered;
+
+ /* Command handlers */
+ STAILQ_HEAD(irc_conn_handlers, irc_cmd_chain) handlers;
};
// XXX: this should probably be slightly reworked
@@ -34,16 +44,31 @@
const char *realname;
};
-/*
+/**
* Create a new irc_conn using the given sock_stream, which should be connected to an IRC server. The parameters given
* in \a config will be used to identify with the IRC server.
*
* On success, the resulting irc_conn is returned via *conn with SUCCESS. Otherwise, -ERR_* and error info is returned
* via *err.
+ *
+ * @param conn the new irc_conn structure is returned via this pointer
+ * @param sock the socket connected to the IRC server
+ * @param config the basic information used to register
+ * @param err errors are returned via this pointer
+ * @return error code
*/
err_t irc_conn_create (struct irc_conn **conn, struct sock_stream *sock, const struct irc_conn_config *config, struct error_info *err);
/**
+ * Add a new chain of command handler callbacks to be used to handle irc_lines from the server. The given arg will be
+ * passed to the callbacks as the context argument. The chain will be appended to the end of the current list of chains.
+ *
+ * @param chain the array of irc_cmd_handler structs, terminated with a NULL entry
+ * @param arg the context argument to use for the callbacks
+ */
+err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg);
+
+/**
* @group Simple request functions
*
* The error handling of these functions is such that the error return code is can be used or ignored as convenient,
@@ -52,26 +77,46 @@
* @{
*/
-/*
+/**
* Send a generic IRC message
+ *
+ * @param conn the IRC protocol connection
+ * @param line the irc_line protocol line to send
+ * @return error code
*/
err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line);
-/*
+/**
* Send a NICK message
+ *
+ * @param nickname the new nickname to use
*/
err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname);
/*
* Send a USER message
+ *
+ * @param username the username to register with, may be replaced with ident reply
+ * @param realname the full-name to register with
*/
err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname);
/*
* Send a PONG message to the given target
+ *
+ * @param target the PING source, aka. the target to send the PONG reply to
*/
err_t irc_conn_PONG (struct irc_conn *conn, const char *target);
+/*
+ * Send a JOIN message for the given channel
+ *
+ * XXX: this doesn't implement the full command options
+ *
+ * @param channel the full channel name to join
+ */
+err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel);
+
// @}
#endif /* IRC_CONN_H */
--- a/src/irc_line.c Sun Mar 01 02:02:48 2009 +0200
+++ b/src/irc_line.c Sun Mar 08 17:17:37 2009 +0200
@@ -55,7 +55,7 @@
len = token_len + (flags & TOK_NOSPACE ? 0 : 1) + (flags & TOK_TRAILING ? 1 : 0);
// check overflow
- if (*offset + len > IRC_LINE_MAX)
+ if (*offset + len + 1 > IRC_LINE_MAX)
return ERR_LINE_TOO_LONG;
// check invalid tokens
@@ -112,6 +112,9 @@
}
}
+ // terminate, output_token reserved the space for this
+ buf[off] = '\0';
+
// done
return SUCCESS;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_log.c Sun Mar 08 17:17:37 2009 +0200
@@ -0,0 +1,58 @@
+#include "irc_log.h"
+#include "log.h"
+
+// XXX: fix this err_t crap
+#define LIB_ERR_H
+#include <evsql.h>
+
+/**
+ * The core irc_log state
+ */
+static struct irc_log_ctx {
+ /** The database connection */
+ struct evsql *db;
+
+} _ctx;
+
+static void on_PRIVMSG (struct irc_conn *conn, const struct irc_line *line, void *arg)
+{
+ struct irc_log_ctx *ctx = arg;
+
+ // log it! :P
+ log_debug("%s: %s: %s", line->prefix, line->args[0], line->args[1]);
+}
+
+static struct irc_cmd_handler _cmd_handlers[] = {
+ { "PRIVMSG", &on_PRIVMSG },
+ { NULL, NULL }
+};
+
+err_t irc_log_init (struct event_base *ev_base, const char *db_info, struct irc_conn *irc, const char *channel)
+{
+ struct irc_log_ctx *ctx = &_ctx;
+ err_t err;
+
+ // open the database connection
+ if (db_info) {
+ log_info("connect to database: %s", db_info);
+
+ if ((ctx->db = evsql_new_pq(ev_base, db_info, NULL, NULL)) == NULL)
+ return ERR_EVSQL_NEW_PQ;
+ }
+
+ if (channel) {
+ log_info("join channel: %s", channel);
+
+ // join the channel
+ if ((err = irc_conn_JOIN(irc, channel)))
+ return err;
+ }
+
+ // register for events
+ if ((err = irc_conn_register_handler_chain(irc, _cmd_handlers, ctx)))
+ return err;
+
+ // ok
+ return SUCCESS;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_log.h Sun Mar 08 17:17:37 2009 +0200
@@ -0,0 +1,21 @@
+#ifndef IRC_LOG_H
+#define IRC_LOG_H
+
+/**
+ * @file
+ *
+ * Logging IRC events to an SQL database
+ */
+
+#include "error.h"
+#include "irc_conn.h"
+#include <event2/event.h>
+
+/**
+ * Initialize the global irc_log state
+ *
+ * XXX: db_info is still unused if not specified
+ */
+err_t irc_log_init (struct event_base *ev_base, const char *db_info, struct irc_conn *irc, const char *channel);
+
+#endif
--- a/src/nexus.c Sun Mar 01 02:02:48 2009 +0200
+++ b/src/nexus.c Sun Mar 08 17:17:37 2009 +0200
@@ -2,6 +2,7 @@
#include "log.h"
#include "sock.h"
#include "irc_conn.h"
+#include "irc_log.h"
#include <stdlib.h>
#include <stdbool.h>
@@ -15,12 +16,21 @@
#define DEFAULT_PORT "6667"
#define DEFAULT_PORT_SSL "6697"
+enum option_code {
+ _OPT_LOG_BEGIN = 0x00ff,
+
+ OPT_LOG_DATABASE,
+ OPT_LOG_CHANNEL,
+};
+
static struct option options[] = {
- {"help", 0, NULL, 'h' },
- {"hostname", 1, NULL, 'H' },
- {"port", 1, NULL, 'P' },
- {"ssl", 0, NULL, 'S' },
- {0, 0, 0, 0 },
+ {"help", 0, NULL, 'h' },
+ {"hostname", 1, NULL, 'H' },
+ {"port", 1, NULL, 'P' },
+ {"ssl", 0, NULL, 'S' },
+ {"log-database", 1, NULL, OPT_LOG_DATABASE },
+ {"log-channel", 1, NULL, OPT_LOG_CHANNEL },
+ {0, 0, 0, 0 },
};
void usage (const char *exe)
@@ -31,6 +41,8 @@
printf(" --hostname / -H HOST set hostname to connect to\n");
printf(" --port / -P PORT set service port to connect to\n");
printf(" --ssl / -S use SSL\n");
+ printf(" --log-database database connection string for logging\n");
+ printf(" --log-channel channel to log\n");
}
int main (int argc, char **argv)
@@ -48,6 +60,7 @@
.username = "spbot-dev",
.realname = "SpBot (development version)",
};
+ const char *log_database = NULL, *log_channel = NULL;
bool port_default = true;
@@ -74,6 +87,14 @@
portname = DEFAULT_PORT_SSL;
break;
+
+ case OPT_LOG_DATABASE:
+ log_database = optarg;
+ break;
+
+ case OPT_LOG_CHANNEL:
+ log_channel = optarg;
+ break;
case '?':
usage(argv[0]);
@@ -109,6 +130,12 @@
// create the irc connection state
if (irc_conn_create(&conn, sock, &conn_config, &err))
FATAL_ERROR(&err, "irc_conn_create");
+
+ // logging?
+ if (log_database || log_channel) {
+ if ((ERROR_CODE(&err) = irc_log_init(ev_base, log_database, conn, log_channel)))
+ FATAL_ERROR(&err, "irc_log_init");
+ }
// run event loop
if (event_base_dispatch(ev_base))