some of the lib/transport stuff compiles new-lib-errors tip
authorTero Marttila <terom@fixme.fi>
Thu, 28 May 2009 01:17:36 +0300
branchnew-lib-errors
changeset 219 cefec18b8268
parent 218 5229a5d098b2
some of the lib/transport stuff compiles
src/CMakeLists.txt
src/fifo.c
src/fifo.h
src/irc_conn.h
src/irc_net.h
src/irc_queue.h
src/lib/error.h
src/lib/errors.c
src/lib/errors.h
src/lib/fifo.c
src/lib/fifo.h
src/lib/line_proto.c
src/lib/line_proto.h
src/lib/lua_func.c
src/lib/lua_func.h
src/lib/lua_type.c
src/lib/lua_type.h
src/lib/msg_proto.c
src/lib/msg_proto.h
src/lib/resolve.c
src/lib/resolve.h
src/lib/service.c
src/lib/service.h
src/lib/service_internal.h
src/lib/sock.c
src/lib/sock.h
src/lib/sock_internal.h
src/lib/ssl.c
src/lib/ssl.h
src/lib/ssl_client.c
src/lib/ssl_internal.h
src/lib/tcp.c
src/lib/tcp.h
src/lib/tcp_client.c
src/lib/tcp_internal.h
src/lib/tcp_server.c
src/lib/tcp_transport.c
src/lib/transport.c
src/lib/transport.h
src/lib/transport_fd.c
src/lib/transport_fd.h
src/lib/transport_internal.h
src/lib/transport_test.c
src/lib/transport_test.h
src/line_proto.c
src/line_proto.h
src/lua_func.c
src/lua_func.h
src/lua_irc.h
src/lua_objs.c
src/lua_objs.h
src/lua_type.c
src/lua_type.h
src/msg_proto.c
src/msg_proto.h
src/resolve.c
src/resolve.h
src/service.c
src/service.h
src/service_internal.h
src/sock.c
src/sock.h
src/sock_internal.h
src/spbot/lua_objs.c
src/spbot/lua_objs.h
src/spbot/nexus.c
src/spbot/nexus_lua.c
src/ssl.c
src/ssl.h
src/ssl_client.c
src/ssl_internal.h
src/tcp.c
src/tcp.h
src/tcp_client.c
src/tcp_internal.h
src/tcp_server.c
src/tcp_transport.c
src/transport.c
src/transport.h
src/transport_fd.c
src/transport_fd.h
src/transport_internal.h
src/transport_test.c
src/transport_test.h
--- a/src/CMakeLists.txt	Thu May 28 00:35:02 2009 +0300
+++ b/src/CMakeLists.txt	Thu May 28 01:17:36 2009 +0300
@@ -14,13 +14,13 @@
 
 # define our source code modules
 set (LIB_SOURCES lib/error.c lib/log.c lib/str.c lib/object.c)
-set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c ssl.c ssl_client.c fifo.c)
-set (PROTO_SOURCES line_proto.c msg_proto.c)
+set (IO_SOURCES lib/transport.c lib/service.c lib/transport_fd.c lib/sock.c lib/resolve.c lib/tcp.c lib/tcp_transport.c lib/tcp_client.c lib/tcp_server.c lib/ssl.c lib/ssl_client.c lib/fifo.c)
+set (PROTO_SOURCES lib/line_proto.c lib/msg_proto.c)
 set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c irc_net_connect.c)
-set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c lua_thread.c)
-set (CONSOLE_SOURCES console.c lua_console.c)
+set (LUA_SOURCES spbot/lua_objs.c spbot/lua_config.c lua_irc.c lib/lua_func.c lib/lua_type.c spbot/lua_thread.c)
+set (CONSOLE_SOURCES lib/console.c spbot/lua_console.c)
 
-set (SPBOT_SOURCES spbot/nexus.c spbot/signals.c spbot/module.c spbot/config.c)
+set (SPBOT_SOURCES spbot/nexus.c spbot/signals.c spbot/module.c spbot/config.c spbot/nexus_lua.c)
 
 set (NEXUS_SOURCES ${SPBOT_SOURCES} ${LIB_SOURCES} ${IO_SOURCES} ${PROTO_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES})
 set (IRC_LOG_SOURCES modules/irc_log.c)
@@ -61,7 +61,7 @@
 if (ENABLE_TEST)
     # build list of source files
     file (GLOB _TEST_SOURCES "test/*.c")
-    set (TEST_SOURCES ${_TEST_SOURCES} ${CORE_SOURCES} ${IO_SOURCES} ${PROTO_SOURCES} transport_test.c ${IRC_SOURCES})
+    set (TEST_SOURCES ${_TEST_SOURCES} ${CORE_SOURCES} ${IO_SOURCES} ${PROTO_SOURCES} lib/transport_test.c ${IRC_SOURCES})
     
     # add executable target and link against libs
     add_executable (test_harness EXCLUDE_FROM_ALL ${TEST_SOURCES})
--- a/src/fifo.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-
-#include "fifo.h"
-#include "transport_fd.h"
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-
-/**
- * Our transport_type
- */
-extern const struct transport_type fifo_type;
-
-/**
- * The fifo state
- */
-struct fifo {
-    /** The FD-based state */
-    struct transport_fd base_fd;
-    
-    /** Path to the fifo */
-    char *path;
-};
-
-/**
- * (re)open the fifo, closing it if already open, and keeping any event callbacks registered.
- */
-static err_t fifo_open (struct fifo *fifo, error_t *err)
-{
-    int _fd;
-
-    // open(2) the path in non-blocking read-only mode
-    if ((_fd = open(fifo->path, O_RDONLY | O_NONBLOCK)) < 0)
-        RETURN_SET_ERROR_ERRNO(err, ERR_OPEN);
-
-    // set the new fd
-    if ((ERROR_CODE(err) = transport_fd_set(&fifo->base_fd, _fd)))
-        return ERROR_CODE(err);
-    
-    // use default transport event-based behaviour
-    if ((ERROR_CODE(err) = transport_fd_defaults(&fifo->base_fd)))
-        return ERROR_CODE(err);
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Deinit the fifo, releasing all resources
- */
-static void fifo_deinit (struct fifo *fifo)
-{
-    // deinit base
-    transport_fd_deinit(&fifo->base_fd);
-
-    // release the path
-    free(fifo->path);
-
-    fifo->path = NULL;
-}
-
-/**
- * sock_stream_methods::read implementation.
- *
- * Try and do a normal sock_fd_read call, but re-open with EAGAIN on EOF
- */
-err_t fifo_read (transport_t *transport, void *buf, size_t *len, struct error_info *err)
-{
-    struct fifo *fifo = transport_check(transport, &fifo_type);
-
-    // trap READ_EOF
-    if (transport_fd__read(transport, buf, len, err) != ERR_EOF)
-        return ERROR_CODE(err);
-    
-    // re-open it
-    // XXX: re-add events?
-    if (fifo_open(fifo, err))
-        goto error;
-
-    // ok, act as if it was EAGAIN
-    *len = 0;
-
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(err);
-}
-
-/**
- * sock_stream_methods::release implementation
- */
-static void fifo__deinit (transport_t *transport)
-{
-    struct fifo *fifo = transport_check(transport, &fifo_type);
-    
-    fifo_deinit(fifo);
-}
-
-/*
- * Our sock_stream_type
- */
-const struct transport_type fifo_type = {
-    .base_type = {
-        .parent     = &transport_fd_type.base_type,
-    },
-    .methods = {
-        .read       = fifo_read,
-        .write      = NULL,
-        .events     = transport_fd__events,
-        .deinit     = fifo__deinit,
-    },
-};
-
-/**
- * Deinit and free
- */
-static void fifo_destroy (struct fifo *fifo)
-{
-    fifo_deinit(fifo);
-
-    free(fifo);
-}
-
-err_t fifo_open_read (struct transport_info *transport_info, transport_t **transport_ptr, struct event_base *ev_base,
-        const char *path, error_t *err)
-{
-    struct fifo *fifo;
-
-    // alloc
-    if ((fifo = calloc(1, sizeof(*fifo))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // copy the path
-    if ((fifo->path = strdup(path)) == NULL)
-        return SET_ERROR(err, ERR_STRDUP);
-
-    // init
-    transport_init(&fifo->base_fd.base, &fifo_type, transport_info);
-    transport_fd_init(&fifo->base_fd, ev_base, TRANSPORT_FD_INVALID);
-    
-    // open the fifo
-    if (fifo_open(fifo, err))
-        goto error;
-
-    // ok
-    *transport_ptr = &fifo->base_fd.base;
-
-    return SUCCESS;
-
-error:
-    // cleanup
-    fifo_destroy(fifo);
-
-    return ERROR_CODE(err);
-}
--- a/src/fifo.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#ifndef FIFO_H
-#define FIFO_H
-
-#include "transport.h"
-
-#include <event2/event.h>
-
-/**
- * A read-only transport based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
- *
- * The transport will be ready for use right away, transport_callbacks::on_connect will never be called.
- *
- * @param transport_info the setup info required to create the transport
- * @param transport_ptr returned transport
- * @param path the path to the filesystem fifo object
- * @param err returned error info
- */
-err_t fifo_open_read (struct transport_info *transport_info, transport_t **transport_ptr, struct event_base *ev_base,
-        const char *path, error_t *err);
-
-
-#endif
--- a/src/irc_conn.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/irc_conn.h	Thu May 28 01:17:36 2009 +0300
@@ -14,8 +14,8 @@
 struct irc_conn;
 
 #include "error.h"
-#include "transport.h"
-#include "line_proto.h"
+#include <lib/transport.h>
+#include <lib/line_proto.h>
 #include "irc_queue.h"
 #include "irc_line.h"
 #include "irc_cmd.h"
--- a/src/irc_net.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/irc_net.h	Thu May 28 01:17:36 2009 +0300
@@ -10,10 +10,10 @@
 
 struct irc_net;
 
-#include "error.h"
 #include "irc_conn.h"
 #include "irc_chan.h"
-#include "ssl.h"
+#include <lib/error.h>
+#include <lib/ssl.h>
 #include <sys/queue.h>
 
 /**
--- a/src/irc_queue.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/irc_queue.h	Thu May 28 01:17:36 2009 +0300
@@ -9,8 +9,8 @@
  * This implements the basic flood control algorithm as described in RFC1459 section 8.10.
  */
 #include "irc_line.h"
-#include "line_proto.h"
-#include "error.h"
+#include <lib/line_proto.h>
+#include <lib/error.h>
 
 #include <sys/queue.h>
 #include <event2/event.h>
--- a/src/lib/error.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/lib/error.h	Thu May 28 01:17:36 2009 +0300
@@ -89,7 +89,7 @@
 #define ERROR_TYPE_STRING(code, name) \
     { (code), (name), &error_extra_string, NULL }
 
-#define ERROR_TYPE_CUSTOM(code, name, type) \
+#define ERROR_TYPE_EXTRA(code, name, type) \
     { (code), (name), (type), NULL }
 
 #define ERROR_TYPE_SUB(code, name, sub) \
@@ -239,7 +239,7 @@
 
 /** Set the error with extra info as integer */
 #define SET_ERROR_EXTRA(err_state, err_list, err_code, err_extra_type, err_extra_int) ({ \
-        _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra);  \
+        _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra_int);  \
         err_code; })
 
 /** Set the error with extra info as the libc errno */
--- a/src/lib/errors.c	Thu May 28 00:35:02 2009 +0300
+++ b/src/lib/errors.c	Thu May 28 01:17:36 2009 +0300
@@ -9,7 +9,12 @@
 );
 
 const struct error_list libc_errors = ERROR_LIST("libc",
-    ERROR_TYPE_ERRNO(   ERR_SIGACTION,          "sigaction"                         )
+    ERROR_TYPE_ERRNO(   ERR_SIGACTION,          "sigaction"                         ),
+    ERROR_TYPE_ERRNO(   ERR_READ,               "read"                              ),
+    ERROR_TYPE_ERRNO(   ERR_WRITE,              "write"                             ),
+    ERROR_TYPE_ERRNO(   ERR_WRITE_EOF,          "write: EOF"                        ),
+    ERROR_TYPE_ERRNO(   ERR_FCNTL,              "fcntl"                             ),
+    ERROR_TYPE_ERRNO(   ERR_CLOSE,              "close"                             )
 );
 
 const struct error_list libevent_errors = ERROR_LIST("libevent",
@@ -18,3 +23,10 @@
     ERROR_TYPE(         ERR_EVENT_DEL,          "event_del"                         )
 );
 
+const struct error_list lua_errors = ERROR_LIST("lua",
+    ERROR_TYPE_STRING(  ERR_LUA_MEM,            "memory error"                      ),
+    ERROR_TYPE_STRING(  ERR_LUA_SYNTAX,         "syntax error"                      ),
+    ERROR_TYPE_STRING(  ERR_LUA_RUN,            "runtime erorr"                     ),
+    ERROR_TYPE_STRING(  ERR_LUA_ERR,            "error-handling error"              ),
+    ERROR_TYPE_STRING(  ERR_LUA_FILE,           "filesystem error"                  )
+);
--- a/src/lib/errors.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/lib/errors.h	Thu May 28 01:17:36 2009 +0300
@@ -34,6 +34,11 @@
 enum libc_error_code {
     ERR_LIBC_NONE,
     ERR_SIGACTION,          ///< sigaction: <perror>
+    ERR_READ,               ///< read: <perror>
+    ERR_WRITE,              ///< write: <perror>
+    ERR_WRITE_EOF,          ///< write: EOF
+    ERR_FCNTL,              ///< fcntl: <perror>
+    ERR_CLOSE,              ///< close: <perror>
 };
 
 const struct error_list libc_errors;
@@ -42,6 +47,7 @@
  * Errors for libevent
  */
 enum libevent_error_code {
+    ERR_EVENT_NONE,
     ERR_EVENT_NEW,          ///< event_new
     ERR_EVENT_ADD,          ///< event_add
     ERR_EVENT_DEL,          ///< event_del
@@ -49,4 +55,18 @@
 
 const struct error_list libevent_errors;
 
+/**
+ * Errors for lua
+ */
+enum lua_error_code {
+    ERR_LUA_NONE,
+    ERR_LUA_MEM,            ///< memory error
+    ERR_LUA_SYNTAX,         ///< syntax error
+    ERR_LUA_RUN,            ///< runtime erorr
+    ERR_LUA_ERR,            ///< error-handling error
+    ERR_LUA_FILE,           ///< filesystem error
+};
+
+const struct error_list lua_errors;
+
 #endif /* LIBQMSK_ERRORS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/fifo.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,157 @@
+
+#include "fifo.h"
+#include "transport_fd.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+/**
+ * Our transport_type
+ */
+extern const struct transport_type fifo_type;
+
+/**
+ * The fifo state
+ */
+struct fifo {
+    /** The FD-based state */
+    struct transport_fd base_fd;
+    
+    /** Path to the fifo */
+    char *path;
+};
+
+/**
+ * (re)open the fifo, closing it if already open, and keeping any event callbacks registered.
+ */
+static err_t fifo_open (struct fifo *fifo, error_t *err)
+{
+    int _fd;
+
+    // open(2) the path in non-blocking read-only mode
+    if ((_fd = open(fifo->path, O_RDONLY | O_NONBLOCK)) < 0)
+        RETURN_SET_ERROR_ERRNO(err, ERR_OPEN);
+
+    // set the new fd
+    if ((ERROR_CODE(err) = transport_fd_set(&fifo->base_fd, _fd)))
+        return ERROR_CODE(err);
+    
+    // use default transport event-based behaviour
+    if ((ERROR_CODE(err) = transport_fd_defaults(&fifo->base_fd)))
+        return ERROR_CODE(err);
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Deinit the fifo, releasing all resources
+ */
+static void fifo_deinit (struct fifo *fifo)
+{
+    // deinit base
+    transport_fd_deinit(&fifo->base_fd);
+
+    // release the path
+    free(fifo->path);
+
+    fifo->path = NULL;
+}
+
+/**
+ * sock_stream_methods::read implementation.
+ *
+ * Try and do a normal sock_fd_read call, but re-open with EAGAIN on EOF
+ */
+err_t fifo_read (transport_t *transport, void *buf, size_t *len, struct error_info *err)
+{
+    struct fifo *fifo = transport_check(transport, &fifo_type);
+
+    // trap READ_EOF
+    if (transport_fd__read(transport, buf, len, err) != ERR_EOF)
+        return ERROR_CODE(err);
+    
+    // re-open it
+    // XXX: re-add events?
+    if (fifo_open(fifo, err))
+        goto error;
+
+    // ok, act as if it was EAGAIN
+    *len = 0;
+
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);
+}
+
+/**
+ * sock_stream_methods::release implementation
+ */
+static void fifo__deinit (transport_t *transport)
+{
+    struct fifo *fifo = transport_check(transport, &fifo_type);
+    
+    fifo_deinit(fifo);
+}
+
+/*
+ * Our sock_stream_type
+ */
+const struct transport_type fifo_type = {
+    .base_type = {
+        .parent     = &transport_fd_type.base_type,
+    },
+    .methods = {
+        .read       = fifo_read,
+        .write      = NULL,
+        .events     = transport_fd__events,
+        .deinit     = fifo__deinit,
+    },
+};
+
+/**
+ * Deinit and free
+ */
+static void fifo_destroy (struct fifo *fifo)
+{
+    fifo_deinit(fifo);
+
+    free(fifo);
+}
+
+err_t fifo_open_read (struct transport_info *transport_info, transport_t **transport_ptr, struct event_base *ev_base,
+        const char *path, error_t *err)
+{
+    struct fifo *fifo;
+
+    // alloc
+    if ((fifo = calloc(1, sizeof(*fifo))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // copy the path
+    if ((fifo->path = strdup(path)) == NULL)
+        return SET_ERROR(err, ERR_STRDUP);
+
+    // init
+    transport_init(&fifo->base_fd.base, &fifo_type, transport_info);
+    transport_fd_init(&fifo->base_fd, ev_base, TRANSPORT_FD_INVALID);
+    
+    // open the fifo
+    if (fifo_open(fifo, err))
+        goto error;
+
+    // ok
+    *transport_ptr = &fifo->base_fd.base;
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    fifo_destroy(fifo);
+
+    return ERROR_CODE(err);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/fifo.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,22 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+#include "transport.h"
+
+#include <event2/event.h>
+
+/**
+ * A read-only transport based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
+ *
+ * The transport will be ready for use right away, transport_callbacks::on_connect will never be called.
+ *
+ * @param transport_info the setup info required to create the transport
+ * @param transport_ptr returned transport
+ * @param path the path to the filesystem fifo object
+ * @param err returned error info
+ */
+err_t fifo_open_read (struct transport_info *transport_info, transport_t **transport_ptr, struct event_base *ev_base,
+        const char *path, error_t *err);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/line_proto.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,336 @@
+
+#include "line_proto.h"
+#include "log.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/*
+ * Our state
+ */
+struct line_proto {
+    /* The transport we read/write with */
+    transport_t *transport;
+
+    /* The incoming/outgoing line buffer */
+    char *in_buf, *out_buf;
+
+    /* Buffer size (same for both) */
+    size_t buf_len;
+
+    /* Offset of trailing data in buf */
+    size_t tail_offset;
+
+    /* Length of trailing data in buf, if any */
+    size_t tail_len;
+
+    /* Amount of data in the out buffer */
+    size_t out_offset;
+
+    /* Last error */
+    struct error_info err;
+
+    /* Callback info */
+    struct line_proto_callbacks callbacks;
+    void *cb_arg;
+};
+
+/**
+ * An error occured which we could not recover from; the line_proto should now be considered corrupt.
+ *
+ * Notify the user callback, which will probably call line_proto_release().
+ */
+static void line_proto_set_error (struct line_proto *lp)
+{
+    // copy error_info, as it might get free'd
+    struct error_info err = lp->err;
+
+    // trigger callback
+    lp->callbacks.on_error(&err, lp->cb_arg);
+}
+
+/**
+ * Our transport_callbacks::on_read handler
+ */
+static void line_proto_on_read (transport_t *transport, void *arg)
+{
+    struct line_proto *lp = arg;
+    char *line;
+
+    (void) transport;
+
+    // sanity-check
+    assert(lp->tail_offset < lp->buf_len);
+    
+    do {
+        // attempt to read a line
+        if (line_proto_recv(lp, &line))
+            // faaail
+            return line_proto_set_error(lp);
+
+        // got a line?
+        if (line)
+            lp->callbacks.on_line(line, lp->cb_arg);
+
+    } while (line);
+}
+
+/*
+ * Signal for write
+ */
+static void line_proto_on_write (transport_t *transport, void *arg)
+{
+    struct line_proto *lp = arg;
+    int ret;
+
+    (void) transport;
+
+    // just flush
+    if ((ret = line_proto_flush(lp)) < 0)
+        // faaail
+        return line_proto_set_error(lp);
+}
+
+// XXX: implement on_error!
+static const struct transport_callbacks line_proto_transport_callbacks = {
+    .on_read    = &line_proto_on_read,
+    .on_write   = &line_proto_on_write,
+};
+
+err_t line_proto_create (struct line_proto **lp_ptr, transport_t *transport, size_t buf_size,
+        const struct line_proto_callbacks *callbacks, void *cb_arg, error_t *err)
+{
+    struct line_proto *lp;
+
+    // alloc
+    if ((lp = calloc(1, sizeof(*lp))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lp->transport = transport;
+    lp->buf_len = buf_size;
+    lp->callbacks = *callbacks;
+    lp->cb_arg = cb_arg;
+
+    // allocate buffers
+    if (
+            (lp->in_buf = malloc(buf_size)) == NULL
+        ||  (lp->out_buf = malloc(buf_size)) == NULL
+    )
+        JUMP_SET_ERROR(err, ERR_CALLOC);
+
+    // setup the transport
+    transport_set_callbacks(transport, &line_proto_transport_callbacks, lp);
+    
+    if ((ERROR_CODE(err) = transport_events(transport, TRANSPORT_READ | TRANSPORT_WRITE)))
+        goto error;
+
+    // return
+    *lp_ptr = lp;
+
+    return SUCCESS;
+
+error:
+    // cleanup the lp
+    line_proto_destroy(lp);
+
+    return ERROR_CODE(err);
+}
+
+/*
+ * This looks for a full '\r\n' terminated line at the beginning of the given buffer. If found, the \r\n will be
+ * replaced with a '\0', and the offset to the beginning of the next line returned. If not found, zero is returned
+ * (which is never a valid next-line offset).
+ *
+ * The given \a hint is an hint as to the offset at which to start scanning, used for incremental invocations of this
+ * on the same buffer.
+ *
+ */
+int _parse_line (char *buf, size_t len, size_t *hint) {
+    size_t i, next = 0;
+
+    // empty buffer -> nothing
+    if (len == 0)
+        return 0;
+
+    // look for terminating '\r\n' or '\n' sequence
+    for (i = *hint; i < len; i++) {
+        // match this + next char?
+        if (i < len - 1 && buf[i] == '\r' && buf[i + 1] == '\n') {
+            next = i + 2;
+            break;
+
+        } else if (buf[i] == '\n') {
+            next = i + 1;
+            break;
+        }
+    }
+
+    // searched the whole buffer?
+    if (i >= len) {
+        // do continue one char back, to keep any \r
+        *hint = len - 1;
+        return 0;
+    }
+
+    // mangle the newline off
+    buf[i] = '\0';
+
+    // return offset to next line, as set in loop based on delim
+    return next;
+}
+
+err_t line_proto_recv (struct line_proto *lp, char **line_ptr)
+{
+    // offset to recv() new data into, offset to _parse_line hint, offset to next line from _parse_line
+    size_t recv_offset = 0, peek_offset = 0, next_offset = 0;
+    int ret;
+
+    // adjust offset to beyond previous data (as will be moved next)
+    recv_offset = lp->tail_len;
+
+    // move trailing data from previous line to front of buffer
+    if (lp->tail_offset) {
+        // move to front, no-op if tail_len is zero
+        memmove(lp->in_buf, lp->in_buf + lp->tail_offset, lp->tail_len);
+
+        // reset
+        lp->tail_offset = 0;
+        lp->tail_len = 0;
+    }
+
+    // readline loop
+    do {
+        // parse any line at the beginning of the buffer
+        if ((next_offset = _parse_line(lp->in_buf, recv_offset, &peek_offset)) > 0) {
+            // store a valid *line_ptr
+            *line_ptr = lp->in_buf;
+            
+            // exit loop and return
+            break;
+        }
+
+        // ensure there's enough space for the rest of the line
+        if (recv_offset >= lp->buf_len)
+            return ERR_LINE_TOO_LONG;
+        
+        // otherwise, read more data
+        if ((ret = transport_read(lp->transport, lp->in_buf + recv_offset, lp->buf_len - recv_offset, &lp->err)) < 0)
+            return ERROR_CODE(&lp->err);
+
+        // EAGAIN?
+        if (ret == 0) {
+            // return a NULL *line_ptr
+            *line_ptr = NULL;
+            break;
+        }
+        
+        // update recv_offset
+        recv_offset += ret;
+
+    } while (1);
+
+    // update state for next call
+    lp->tail_offset = next_offset;
+    lp->tail_len = recv_offset - next_offset;
+
+    // ok
+    return SUCCESS;
+}
+
+int line_proto_send (struct line_proto *lp, const char *line)
+{
+    int ret;
+    size_t len = strlen(line), ret_len;
+
+    // drop line if we already have output buffered
+    if (lp->out_offset)
+        return -ERR_LINE_TOO_LONG;
+    
+    // try and write the line
+    if ((ret = transport_write(lp->transport, line, len, &lp->err)) < 0)
+        return -ERROR_CODE(&lp->err);
+
+    // length of the sent data
+    ret_len = ret;
+
+    // EAGAIN or partial?
+    if (ret_len < len) {
+        size_t trailing = len - ret_len;
+
+        // ensure it's not waaaay too long
+        if (trailing > lp->buf_len)
+            return -ERR_LINE_TOO_LONG;
+
+        // copy remaining portion to buffer
+        memcpy(lp->out_buf, line + ret_len, trailing);
+
+        // update offset
+        lp->out_offset = trailing;
+        
+        // buffered... transport should invoke on_write itself
+        return 1;
+
+    } else {
+        // ok, no buffering needed
+        return SUCCESS;
+
+    }
+}
+
+int line_proto_flush (struct line_proto *lp)
+{
+    int ret;
+    size_t ret_len;
+
+    assert(lp->out_offset);
+
+    // try and write the line
+    if ((ret = transport_write(lp->transport, lp->out_buf, lp->out_offset, &lp->err)) < 0)
+        return -ERROR_CODE(&lp->err);
+
+    ret_len = ret;
+
+    // empty now?
+    if (ret_len == lp->out_offset) {
+        lp->out_offset = 0;
+
+        return SUCCESS;
+    }
+
+    // partial?
+    if (ret_len > 0) {
+        size_t remaining = lp->out_offset - ret_len;
+
+        // move the rest up
+        memmove(lp->out_buf, lp->out_buf + ret_len, remaining);
+
+        // update offset
+        lp->out_offset = remaining;
+    }
+
+    // ok
+    return 1;
+}
+
+const struct error_info* line_proto_error (struct line_proto *lp)
+{
+    // return pointer
+    return &lp->err;
+}
+
+void line_proto_destroy (struct line_proto *lp)
+{
+    // free buffers
+    free(lp->in_buf);
+    free(lp->out_buf);
+
+    // socket?
+    if (lp->transport)
+        transport_destroy(lp->transport);
+
+    // free the state itself
+    free(lp);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/line_proto.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,84 @@
+#ifndef LIBQMSK_LINE_PROTO_H
+#define LIBQMSK_LINE_PROTO_H
+
+/**
+ * @file
+ *
+ * Support for protocols that send/receive lines
+ */
+#include "transport.h"
+#include "error.h"
+
+/**
+ * The line_proto state handle
+ */
+struct line_proto;
+
+/**
+ * User callbacks for event-based line_proto behaviour
+ */
+struct line_proto_callbacks {
+    /** Handle received line */
+    void (*on_line) (char *line, void *arg);
+    
+    /** Transport failed, the line_proto is corrupt, you should call line_proto_release next. */
+    void (*on_error) (const error_t *err, void *arg);
+};
+
+/**
+ * Create a new line_proto off the the given sock_stream. The newly allocated line_proto will be returned via *lp_ptr.
+ *
+ * The incoming lines are buffered in a buffer of \a buf_size bytes. This imposes a maximum limit on the line length.
+ *
+ * In case of errors, \a transport will be destroyed in any case.
+ *
+ * @param lp_ptr a pointer to the new line_proto will be returned via this pointer
+ * @param transport the connected transport to use
+ * @param buf_size the incoming/outgoing buffer size, should be enough to hold the biggest possible line
+ * @param callbacks the callbacks to use, a copy is stored
+ * @param cb_arg the read_cb callback argument
+ * @param err error information is returned via this pointer
+ */
+err_t line_proto_create (struct line_proto **lp_ptr, transport_t *transport, size_t buf_size,
+        const struct line_proto_callbacks *callbacks, void *cb_arg, error_t *err);
+
+/**
+ * Runs transport_read() with our internal buffer. If a full line was received, a pointer to our internal bufffer is
+ * returned via *line_ptr, and we return SUCCESS. If we don't yet have a full line, and receiving more would block,
+ * NULL is returned via *line_ptr instead. Otherwise, nonzero error return code.
+ *
+ * @param line_ptr a pointer to the received line is returned via this pointer
+ */
+err_t line_proto_recv (struct line_proto *lp, char **line_ptr);
+
+/**
+ * Write a single line to the sock_stream, buffering any incomplete fragment that remains unsent. Returns zero if the
+ * line was succesfully sent, >0 if it was only partially sent, or -err on errors.
+ *
+ * The given line should already include the terminating '\r\n' character sequence.
+ *
+ * @param line pointer to buffer containing \r\n\0 terminated line
+ */
+int line_proto_send (struct line_proto *lp, const char *line);
+
+/**
+ * Flush out any buffered line fragment. Returns zero if the buffer was flushed empty, >0 if there's still fragments
+ * remaining, or -err on errors.
+ *
+ * It is a bug to call this if there is no data waiting to be sent.
+ */
+int line_proto_flush (struct line_proto *lp);
+
+/**
+ * Get current error_info*
+ */
+const error_t* line_proto_error (struct line_proto *lp);
+
+/**
+ * Destroy any buffers and the underlying transport.
+ *
+ * This does not close the connection cleanly, and is intended for use to abort after errors.
+ */
+void line_proto_destroy (struct line_proto *lp);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/lua_func.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,207 @@
+#include "lua_func.h"
+#include "error.h"
+
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Pushes onto the stack the value at t[i]
+ */
+static void lua_getindex (lua_State *L, int t, int i)
+{
+    lua_pushinteger(L, i);
+    lua_gettable(L, t);
+}
+
+/**
+ * Pushes onto the stack either:
+ *  * the value at t[name]
+ *  * the value at t[index]
+ *
+ *  Returns the new index, or 0, if neither could be found
+ */
+static int lua_arg_lookup (lua_State *L, int t, const char *name, int index)
+{
+    // try name
+    lua_getfield(L, t, name);
+
+    if (!lua_isnil(L, -1))
+        return lua_gettop(L);
+    else
+        lua_pop(L, 1);
+    
+    // try index
+    lua_getindex(L, t, index);
+    
+    if (!lua_isnil(L, -1))
+        return lua_gettop(L);
+
+    else
+        lua_pop(L, 1);
+    
+    // not found
+    return 0;
+}
+
+static const char *_lua_arg_string (lua_State *L, int index, const char *name, const char *def)
+{
+    const char *value;
+
+    // use default?
+    if (lua_isnoneornil(L, index) && def != (const char *) LUA_ARG_REQUIRED)
+        return def;
+    
+    // value given?
+    if ((value = lua_tostring(L, index)))
+        return value;
+   
+    // error
+    luaL_error(L, "missing value for required string argument <%d:%s>", index, name); return NULL;
+}
+
+static bool _lua_arg_bool (lua_State *L, int index, const char *name, int def)
+{
+    (void) name;
+
+    // use default?
+    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
+        return def;
+   
+    // value given
+    return lua_toboolean(L, index);
+}
+
+static long _lua_arg_int (lua_State *L, int index, const char *name, long def)
+{
+    (void) name;
+
+    // use default?
+    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
+        return def;
+   
+    // conver to integer
+    // XXX: check compatibility?
+    return lua_tointeger(L, index);
+}
+
+static void * _lua_arg_obj (lua_State *L, int index, const struct lua_type *type, bool optional)
+{
+    // not given?
+    if (!lua_isnoneornil(L, index))
+        return lua_type_get(L, type, index);
+
+    if (optional)
+        return NULL;
+    
+    luaL_error(L, "missing value for required object argument <%d:%s>", index, type->name);
+    return NULL;
+}
+
+/**
+ * Look up the arg index to use for the given index/name.
+ *
+ * If no value is found for the corresponding index, returns zero.
+ */
+static int lua_arg_index (lua_State *L, int nargs, int index, const char *name)
+{
+    // lookup from table?
+    if (nargs == 2 && lua_istable(L, 2) && name) {
+        // push the value from the named field onto the stack
+        lua_getfield(L, 2, name);
+        
+        // no named field?
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+
+            lua_getindex(L, 2, index - 1);
+        }
+
+        // no index field?
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+
+            return 0;
+        }
+        
+        // found either a named or indexed arg
+        return lua_gettop(L);
+
+    } else if (index <= nargs) {
+        // use the same index
+        return index;
+
+    } else {
+        // no index
+        return 0;
+    }
+}
+
+const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def)
+{
+    return _lua_arg_string(L, lua_arg_index(L, nargs, index, name), name, def);
+}
+
+bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def)
+{
+    return _lua_arg_bool(L, lua_arg_index(L, nargs, index, name), name, def);    
+}
+
+void* lua_arg_obj (lua_State *L, int nargs, int index, const struct lua_type *type, bool optional)
+{
+    return _lua_arg_obj(L, lua_arg_index(L, nargs, index, NULL), type, optional);
+}
+
+long lua_arg_int (lua_State *L, int nargs, int index, const char *name, long def)
+{
+    return _lua_arg_int(L, lua_arg_index(L, nargs, index, name), name, def);
+}
+
+void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...)
+{
+    int argidx = 1, argtbl = 0, idx;
+    const struct lua_func_arg *arg;
+    va_list vargs;
+
+    // first, the obj argument
+    if (func->type)
+        *obj_ptr = lua_type_get(L, func->type, argidx++);
+
+    // were we given a table of arguments?
+    if (lua_istable(L, argidx))
+        argtbl = argidx++;
+
+    // parse the args
+    va_start(vargs, obj_ptr);
+
+    for (arg = func->args, idx = 1; arg->name && arg->type; arg++, idx++) {
+        int index;
+
+        // map index
+        if (!argtbl)
+            // direct
+            index = argidx++;
+        
+        else
+            // lookup from table
+            index = lua_arg_lookup(L, argtbl, arg->name, idx);
+        
+        // apply
+        switch (arg->type) {
+            case LUA_ARG_STRING:
+                *va_arg(vargs, const char **) = _lua_arg_string(L, index, arg->name, arg->def.string);
+                break;
+
+            case LUA_ARG_BOOL:
+                *va_arg(vargs, bool *) = _lua_arg_bool(L, index, arg->name, arg->def.boolean);
+                break;
+            
+            case LUA_ARG_INT:
+                *va_arg(vargs, long *) = _lua_arg_int(L, index, arg->name, arg->def.integer);
+                break;
+
+            default:
+                NOT_REACHED();
+        };
+    }
+
+    va_end(vargs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/lua_func.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,107 @@
+#ifndef LUA_FUNC_H
+#define LUA_FUNC_H
+
+/**
+ * @file
+ *
+ * Convenience functions for working with lua C functions
+ */
+#include "lua_type.h"
+#include <stdbool.h>
+
+/**
+ * Lua function argument types
+ */
+enum lua_arg_type {
+    LUA_ARG_INVALID,
+    
+    /** A `const char *` pointing to lua-GC'd memory */
+    LUA_ARG_STRING,
+
+    /** A c99 `bool` */
+    LUA_ARG_BOOL,
+
+    /** A `long signed int` */
+    LUA_ARG_INT,
+};
+
+/**
+ * Function argument def
+ */
+struct lua_func_arg {
+    /** Argument name */
+    const char *name;
+
+    /** Expected type */
+    enum lua_arg_type type;
+
+    /** Default value */
+    union {
+        const char *string;
+        int boolean;
+        long integer;
+    } def;
+};
+
+/**
+ * Function def
+ */
+struct lua_func {
+    /** Object type, or NULL */
+    const struct lua_type *type;
+
+    /** Function name */
+    const char *name;
+
+    /** Help string */
+    const char *help;
+
+    /** Arguments */
+    const struct lua_func_arg args[];
+};
+
+/**
+ * Used as the "invalid" default value
+ */
+#define LUA_ARG_REQUIRED (-1)
+#define LUA_ARG_STRING_REQUIRED ((const char *) (-1))
+
+/**
+ * Define a function argument
+ */
+#define LUA_FUNC_ARG_STRING(name, def) { (name), LUA_ARG_STRING, { .string = (def) } }
+#define LUA_FUNC_ARG_BOOL(name, def) { (name), LUA_ARG_BOOL, { .boolean = (def) } }
+#define LUA_FUNC_ARG_INT(name, def) { (name), LUA_ARG_INT, { .integer = (def) } }
+#define LUA_FUNC_ARG_END { NULL, 0, { 0 } }
+
+/**
+ * Define a function
+ */
+#define LUA_FUNC(type, name, help, ...) { (type), (name), (help), { __VA_ARGS__, LUA_FUNC_ARG_END } }
+
+/**
+ * Parse and return a string argument
+ */
+const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def);
+
+/**
+ * Parse and return a boolean argument
+ */
+bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def);
+
+/**
+ * Parse and return an integer argument
+ */
+long lua_arg_int (lua_State *L, int nargs, int index, const char *name, long def);
+
+/**
+ * Return a userdata argument at the given fixed index
+ */
+void* lua_arg_obj (lua_State *L, int nargs, int index, const struct lua_type *type, bool optional);
+
+/**
+ * Parse function arguments as defined
+ */
+void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/lua_type.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,69 @@
+#include "lua_type.h"
+
+#include <lua5.1/lauxlib.h>
+
+void lua_type_register (lua_State *L, const struct lua_type *type, const struct lua_method methods[])
+{
+    const struct lua_method *method;
+
+    // create the metatable
+    luaL_newmetatable(L, type->name);
+
+    // set the metatable __index to itself
+    lua_pushvalue(L, -1);
+    lua_setfield(L, -1, "__index");
+
+    // add the methods to the metatable
+    for (method = methods; method->func; method++) {
+        lua_pushcfunction(L, method->func);
+        lua_setfield(L, -2, method->name);
+    }
+}
+
+void* lua_type_create (lua_State *L, const struct lua_type *type, size_t size)
+{
+    // create the new userdata on the stack
+    void *ud = lua_newuserdata(L, size);
+
+    // get the type and set it
+    luaL_getmetatable(L, type->name);
+    lua_setmetatable(L, -2);
+
+    // ok
+    return ud;
+}
+
+void* lua_type_register_global (lua_State *L, const struct lua_type *type, const struct lua_method methods[],
+        const char *global_name, size_t size)
+{
+    // allocate the global object
+    void *obj = lua_newuserdata(L, size);
+    
+    // create the type metatable
+    lua_type_register(L, type, methods);
+
+    // set the userdata's metatable
+    lua_setmetatable(L, -2);
+
+    // store it as a global
+    lua_setglobal(L, global_name);
+    
+    // ok
+    return obj;
+}
+
+void* lua_type_get (lua_State *L, const struct lua_type *type, int index)
+{
+    void *ud;
+
+    // validate the userdata arg
+    // XXX: the luaL_checkudata actually raises an error itself
+    if ((ud = luaL_checkudata(L, index, type->name)) == NULL) {
+        luaL_error(L, "bad type argument: `%s` expected", type->name); return NULL;
+
+    } else {
+        // ok
+        return ud;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/lua_type.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,74 @@
+#ifndef LUA_TYPE_H
+#define LUA_TYPE_H
+
+/**
+ * @file
+ *
+ * Convenience functions for defining "types" in lua
+ */
+#include <lua5.1/lua.h>
+
+// XXX: remove
+#include <lua5.1/lauxlib.h>
+
+/**
+ * A type's method
+ *
+ * XXX: a name field?
+ */
+struct lua_method {
+    /** The name of the method */
+    const char *name;
+
+    /** The function pointer */
+    lua_CFunction func;
+
+    /** The function definition, optional */
+    const struct lua_func *info;
+};
+
+#define LUA_METHOD(name, func, info) \
+    { (name), (func), (info) }
+
+#define LUA_METHODS(...) \
+    { __VA_ARGS__, { NULL, NULL, NULL } }
+
+/**
+ * A type
+ */
+struct lua_type {
+    /** The name of the type */
+    const char *name;
+};
+
+#define LUA_TYPE(name) \
+    { (name) }
+
+/**
+ * Register a new metadata table for the given type in the given lua state.
+ *
+ * This leaves the new type (metatable) on the stack.
+ */
+void lua_type_register (lua_State *L, const struct lua_type *type, const struct lua_method methods[]);
+
+/**
+ * Create a new instance of the given type.
+ *
+ * This leaves the new userdata object on the stack.
+ */
+void* lua_type_create (lua_State *L, const struct lua_type *type, size_t size);
+
+/**
+ * Create a new userdata type, and also create an instance of it, register it as a global, and return it.
+ *
+ * This leaves the new userdata object on the stack.
+ */
+void* lua_type_register_global (lua_State *L, const struct lua_type *type, const struct lua_method methods[],
+        const char *global_name, size_t size);
+
+/**
+ * Get an object of the given type from the given stack position
+ */
+void* lua_type_get (lua_State *L, const struct lua_type *type, int index);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/msg_proto.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,493 @@
+#include "msg_proto.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+/**
+ * I/O buffer
+ */
+struct msg_buf {
+    /** Buffer base pointer */
+    char *base;
+
+    /** Size of the buffer */
+    size_t size;
+
+    /** Current read/write offset */
+    size_t off;
+};
+
+/**
+ * The minimum size used for any msg_buf::size related operation.
+ */
+#define MSG_BUF_MIN_SIZE 1024
+
+/**
+ * Growth rate for size
+ */
+#define MSG_BUF_GROW_RATE 2
+
+/**
+ * Initialize a message buffer at the given initial size
+ */
+err_t msg_buf_init (struct msg_buf *buf, size_t hint)
+{
+    // apply minimum size
+    if (hint < MSG_BUF_MIN_SIZE)
+        hint = MSG_BUF_MIN_SIZE;
+
+    // allocate the initial buffer
+    if ((buf->base = malloc(hint)) == NULL)
+        return ERR_MEM;
+    
+    // set fields
+    buf->size = hint;
+    buf->off = 0;
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Grow the buffer if needed to fit the given capacity.
+ */
+err_t msg_buf_grow (struct msg_buf *buf, size_t size)
+{
+    char *tmp = buf->base;
+
+    if (buf->size >= size)
+        // nothing to do
+        return SUCCESS;
+
+    // calculate new size
+    while (buf->size < size)
+        buf->size *= MSG_BUF_GROW_RATE;
+
+    // resize
+    if ((buf->base = realloc(buf->base, buf->size)) == NULL) {
+        buf->base = tmp;
+
+        return ERR_MEM;
+    }
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Drain \a len bytes off the head of the buffer
+ */
+err_t msg_buf_drain (struct msg_buf *buf, size_t len)
+{
+    // simple memmove
+    memmove(buf->base, buf->base + len, buf->off - len);
+
+    // update offfset
+    buf->off -= len;
+    
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Read into the buffer from a transport_t.
+ *
+ * This will attempt to read \a len bytes onto the end of the buffer, growing it if needed to fit.
+ *
+ * This may read/return more data than the given len. Use msg_buf_drain the remove the data from the buffer once you
+ * have used it.
+ *
+ * Returns the number of new bytes read, zero for transport read buffer empty, -err_t for error.
+ */
+ssize_t msg_buf_read (struct msg_buf *buf, transport_t *transport, size_t len, error_t *err)
+{
+    ssize_t ret;
+
+    // clamp size
+    if (len < MSG_BUF_MIN_SIZE)
+        len = MSG_BUF_MIN_SIZE;
+
+    // ensure space
+    if ((ERROR_CODE(err) = msg_buf_grow(buf, buf->off + len)))
+        goto error;
+
+    // read
+    if ((ret = transport_read(transport, buf->base + buf->off, len, err)) < 0)
+        goto error;
+    
+    // no data left?
+    if (!ret)
+        return 0;
+
+    // update offset
+    buf->off += ret;
+
+    // ok
+    return ret;
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+/**
+ * Drives transport_write on the given data until all the given data is written, or zero is returned.
+ *
+ * @param transport transport to write to
+ * @param data input data
+ * @param len number of bytes to write from data
+ * @param err returned error info
+ * @return number of bytes written (which may be zero or less than len), or -err_t.
+ */
+static ssize_t _transport_write_all (transport_t *transport, const char *data, size_t len, error_t *err)
+{
+    ssize_t ret;
+    size_t written = 0;
+
+    while (len) {
+        // try and write out remaining data
+        if ((ret = transport_write(transport, data, len, err)) < 0)
+            goto error;
+        
+        if (!ret) {
+            // write buffer full
+            break;
+
+        } else { 
+            // update and continue
+            written += ret;
+            data += ret;
+            len -= ret;
+        }
+    }
+
+    // ok
+    return written;
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+/**
+ * If the buffer is empty, this will attempt to write the given data directly using transport_write until either all
+ * the data is written (in which case nothing more needs to be done), or the transport won't accept any more writes,
+ * in which case the remaining data will be buffered.
+ *
+ * If the buffer is not empty, then the given data will be added to the end of the buffer, since otherwise the order of
+ * data would be broken.
+ *
+ * In either case, transport_write semantics garuntee that our buffer will either be empty, or an on_write will be
+ * pending on the transport. See msg_buf_flush() for how to handle transport_callbacks::on_write.
+ */
+err_t msg_buf_write (struct msg_buf *buf, transport_t *transport, const void *data_ptr, size_t len, error_t *err)
+{
+    ssize_t ret;
+    const char *data = data_ptr;
+
+    if (!buf->off) {
+        // no data buffered, so we can try and write directly
+        if ((ret = _transport_write_all(transport, data, len, err)) < 0)
+            goto error;
+    
+        // update written
+        data += ret;
+        len -= ret;
+        
+        if (len == 0)
+            // wrote it all
+            return SUCCESS;
+    }
+
+    // ensure space
+    if ((ERROR_CODE(err) = msg_buf_grow(buf, buf->off + len)))
+        goto error;
+
+    // store
+    memcpy(buf->base + buf->off, data, len);
+    
+    // update
+    buf->off += len;
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);    
+}
+
+/**
+ * Flush buffered write data to the transport, driving transport_write() until either all of our bufferd data has been
+ * written, or the transport will not accept any more.
+ *
+ * In either case, transport_write semantics garuntee that our buffer will either be empty, or an on_write will be
+ * pending on the transport.
+ */
+err_t msg_buf_flush (struct msg_buf *buf, transport_t *transport, error_t *err)
+{
+    ssize_t ret;
+
+    // write
+    if ((ret = _transport_write_all(transport, buf->base, buf->off, err)) < 0)
+        goto error;
+    
+    if (ret)
+        // unbuffer the written data
+        msg_buf_drain(buf, ret);
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);    
+}
+
+/**
+ * Deinitialize msg_buf to release allocated buffers
+ */
+void msg_buf_deinit (struct msg_buf *buf)
+{
+    // release
+    free(buf->base);
+
+    // reset
+    buf->base = NULL;
+    buf->size = buf->off = 0;
+}
+
+/**
+ * Message header
+ */
+struct msg_header {
+    /** Message length, including header */
+    uint16_t len;
+};
+
+/**
+ * Message header size
+ */
+#define MSG_PROTO_HEADER_SIZE (sizeof(uint16_t))
+
+/**
+ * Our state struct
+ */
+struct msg_proto {
+    /** The transport */
+    transport_t *transport;
+
+    /** User callbacks */
+    const struct msg_proto_callbacks *cb_tbl;
+
+    /** User callback argument */
+    void *cb_arg;
+
+    /** Input buffer */
+    struct msg_buf in;
+
+    /** Output buffer */
+    struct msg_buf out;
+};
+
+/**
+ * Signal error to user
+ */
+static void msg_proto_error (struct msg_proto *proto, const error_t *err)
+{
+    // invoke user callback
+    proto->cb_tbl->on_error(proto, err, proto->cb_arg);
+}
+
+/**
+ * Attempt to read the current header from our input buffer.
+ *
+ * Returns >0 for full header, 0 for incomplete header, -err_t for error.
+ */
+static int msg_proto_peek_header (struct msg_proto *proto, struct msg_header *header, error_t *err)
+{
+    if (proto->in.off < MSG_PROTO_HEADER_SIZE)
+        // not enough data for header
+        return 0;
+
+    // read header
+    header->len = ntohs(*((uint16_t *) proto->in.base));
+
+    // bad header?
+    if (header->len < MSG_PROTO_HEADER_SIZE)
+        JUMP_SET_ERROR_STR(err, ERR_MISC, "message_header::len");
+
+    // ok, got header
+    return 1;
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+/**
+ * Recieved a message with the given header, and a pointer to the message data
+ *
+ * XXX: what to do if the user callback destroys the msg_proto?
+ */
+static err_t msg_proto_on_msg (struct msg_proto *proto, struct msg_header *header, char *data, error_t *err)
+{
+    (void) err;
+
+    // invoke user callback
+    proto->cb_tbl->on_msg(proto, data, header->len - MSG_PROTO_HEADER_SIZE, proto->cb_arg);
+
+    // XXX: handle user errors
+    return SUCCESS;
+}
+
+static void msg_proto_on_read (transport_t *transport, void *arg)
+{
+    struct msg_proto *proto = arg;
+    struct msg_header header;
+    ssize_t ret;
+    error_t err;
+    
+    // we might be able to read more than one message per event
+    do {
+        // try and read message length for incomplete message
+        if ((ret = msg_proto_peek_header(proto, &header, &err)) < 0)
+            goto error;
+        
+        // need to read more data?
+        if (!ret || header.len > proto->in.off) {
+            // msg_buf_read a minimum size, so passing a zero is OK
+            size_t to_read = ret ? header.len : 0;
+
+            // read into our buffer
+            if ((ret = msg_buf_read(&proto->in, transport, to_read, &err)) < 0)
+                goto error;
+    
+        } else {
+            // handle full message
+            if (msg_proto_on_msg(proto, &header, proto->in.base + MSG_PROTO_HEADER_SIZE, &err))
+                goto error;
+            
+            // remove the data from the buffer
+            msg_buf_drain(&proto->in, header.len);
+        }
+    } while (ret);
+    
+    // ok
+    return;
+
+error:
+    // notify user
+    msg_proto_error(proto, &err);    
+}
+
+static void msg_proto_on_write (transport_t *transport, void *arg)
+{
+    struct msg_proto *proto = arg;
+    error_t err;
+
+    // flush
+    if (msg_buf_flush(&proto->out, transport, &err))
+        // notify user on transport errors
+        msg_proto_error(proto, &err);
+}
+
+static void msg_proto_on_error (transport_t *transport, const error_t *err, void *arg)
+{
+    struct msg_proto *proto = arg;
+
+    (void) transport;
+
+    // report to user
+    msg_proto_error(proto, err);
+}
+
+static const struct transport_callbacks msg_proto_transport_callbacks = {
+    .on_read    = msg_proto_on_read,
+    .on_write   = msg_proto_on_write,
+    .on_error   = msg_proto_on_error,
+};
+
+err_t msg_proto_create (struct msg_proto **proto_ptr, transport_t *transport, const struct msg_proto_callbacks *cb_tbl, void *cb_arg, error_t *err)
+{
+    struct msg_proto *proto;
+
+    // alloc
+    if ((proto = calloc(1, sizeof(*proto))) == NULL)
+        return ERR_MEM;
+
+    // store
+    proto->transport = transport;
+    proto->cb_tbl = cb_tbl;
+    proto->cb_arg = cb_arg;
+
+    // init
+    if (
+            (ERROR_CODE(err) = msg_buf_init(&proto->in, 0))
+        ||  (ERROR_CODE(err) = msg_buf_init(&proto->out, 0))
+    )
+        goto error;
+
+    // setup transport
+    if ((ERROR_CODE(err) = transport_events(transport, TRANSPORT_READ | TRANSPORT_WRITE)))
+        goto error;
+
+    transport_set_callbacks(transport, &msg_proto_transport_callbacks, proto);
+
+    // ok
+    *proto_ptr = proto;
+
+    return SUCCESS;
+
+error:
+    // release
+    msg_proto_destroy(proto);
+
+    return ERROR_CODE(err);
+}
+
+/**
+ * Build and write out the data for the given header
+ */
+static err_t msg_proto_write_header (struct msg_proto *proto, const struct msg_header *header, error_t *err)
+{
+    char buf[MSG_PROTO_HEADER_SIZE];
+
+    // validate
+    if (header->len < MSG_PROTO_HEADER_SIZE)
+        return SET_ERROR(err, ERR_MISC);
+
+    // build
+    *((uint16_t *) buf) = htons(header->len);
+
+    // write
+    return msg_buf_write(&proto->out, proto->transport, buf, sizeof(buf), err);
+}
+
+err_t msg_proto_send (struct msg_proto *proto, const void *data, size_t len, error_t *err)
+{
+    struct msg_header header;
+
+    // build header
+    header.len = MSG_PROTO_HEADER_SIZE + len;
+
+    // write it
+    if (
+            msg_proto_write_header(proto, &header, err)
+        ||  msg_buf_write(&proto->out, proto->transport, data, len, err)
+    )
+        return ERROR_CODE(err);
+
+    // ok
+    return SUCCESS;
+}
+
+void msg_proto_destroy (struct msg_proto *proto)
+{
+    // drop buffers
+    msg_buf_deinit(&proto->in);
+    msg_buf_deinit(&proto->out);
+
+    // kill transport
+    transport_destroy(proto->transport);
+
+    // release ourself
+    free(proto);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/msg_proto.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,54 @@
+#ifndef LIBQMSK_MSG_PROTO_H
+#define LIBQMSK_MSG_PROTO_H
+
+/**
+ * @param
+ *
+ * Support for simple protocols that send/recieve length-prefixed messages over a transport stream.
+ *
+ * This implementation is mostly geared towards handling a reasonable number of reasonably sized messages in a
+ * reasonable way. Hence, 
+ */
+#include "transport.h"
+
+/** 
+ * Protocol state struct
+ */
+struct msg_proto;
+
+/**
+ * User callbacks
+ */
+struct msg_proto_callbacks {
+    /**
+     * Message recieved.
+     *
+     * XXX: currently you must not call msg_proto_destroy from within this callback
+     */
+    void (*on_msg) (struct msg_proto *proto, void *data, size_t len, void *arg);
+
+    /**
+     * Transport/protocol error occured in event handling.
+     */
+    void (*on_error) (struct msg_proto *proto, const error_t *err, void *arg);
+};
+
+/**
+ * Create a msg_proto state using the given transport.
+ *
+ * This will install our callback handlers on the given transport.
+ */
+err_t msg_proto_create (struct msg_proto **proto_ptr, transport_t *transport, const struct msg_proto_callbacks *cb_tbl, void *cb_arg, error_t *err);
+
+/**
+ * Send a message to the other endpoint
+ */
+err_t msg_proto_send (struct msg_proto *proto, const void *data, size_t len, error_t *err);
+
+/**
+ * Destroy the protocol state and transport
+ */
+void msg_proto_destroy (struct msg_proto *proto);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/resolve.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,109 @@
+#include "resolve.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+
+static const char* _gai_error_msg (const struct error_extra_type *type, const union error_extra *extra)
+{
+    (void) type;
+
+    return gai_strerror(extra->int_);
+}
+
+static const struct error_extra_type _gai_error_type = {
+    .name       = "gai",
+    .msg_func   = _gai_error_msg
+};
+
+
+
+const struct error_list resolve_errors = ERROR_LIST("resolve",
+    ERROR_TYPE_EXTRA(   ERR_RESOLVE_GETADDRINFO,        "getaddrinfo",              &_gai_error_type),
+    ERROR_TYPE_EXTRA(   ERR_RESOLVE_GETADDRINFO_EMPTY,  "getaddrinfo: no results",  &_gai_error_type)
+
+);
+
+err_t resolve_addr (struct resolve_result *res, const char *node, const char *service, int socktype, int ai_flags, error_t *err)
+{
+    struct addrinfo hints, *ai;
+    int ret;
+
+    // build hints
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = ai_flags;
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = socktype;
+
+    // resolve (blocking)
+    if ((ret = getaddrinfo(node, service, &hints, &ai)))
+        return SET_ERROR_EXTRA(err, &resolve_errors, ERR_RESOLVE_GETADDRINFO, &_gai_error_type, ret);
+
+    // no results?
+    if (!ai)
+        return SET_ERROR(err, &resolve_errors, ERR_RESOLVE_GETADDRINFO_EMPTY);
+    
+    // store
+    res->list = res->item = ai;
+
+    // ok
+    return SUCCESS;
+}
+
+void resolve_result_init (struct resolve_result *res)
+{
+    res->list = res->item = NULL;
+}
+
+struct addrinfo* resolve_result_next (struct resolve_result *res)
+{
+    if (!res->item) {
+        // no items left
+        return NULL;
+
+    } else {
+        // ...remember the current item
+        struct addrinfo *ai = res->item;
+
+        if (res->item)
+            // advance item to the next one
+            res->item = res->item->ai_next;
+
+        // return the current one
+        return ai;
+    }
+}
+
+void resolve_result_deinit (struct resolve_result *res)
+{
+    if (res->list)
+        // free them all
+        freeaddrinfo(res->list);
+    
+    // invalidate
+    res->list = res->item = NULL;
+}
+
+const char * resolve_addr_text (const struct addrinfo *addr)
+{
+    static char text[1024];
+    char host[NI_MAXHOST], service[NI_MAXSERV];
+    int ret;
+
+    // lookup the reverse nameinfo
+    if ((ret = getnameinfo(
+                addr->ai_addr, addr->ai_addrlen, 
+                host, sizeof(host), service, sizeof(service), 
+                NI_NUMERICHOST | NI_NUMERICSERV
+    ))) {
+        strcpy(host, "???");
+        strcpy(service, "???");
+    }
+
+    // format message
+    snprintf(text, sizeof(text), "[%s]:%s", host, service);
+
+    // return static pointer
+    return text;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/resolve.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,70 @@
+#ifndef LIBQMSK_RESOLVE_H
+#define LIBQMSK_RESOLVE_H
+
+/**
+ * @file
+ *
+ * DNS resolver interface
+ */
+#include "error.h"
+#include <netdb.h>
+
+/**
+ * Errors
+ */
+enum resolve_error_code {
+    ERR_RESOLVE_NONE,
+    ERR_RESOLVE_GETADDRINFO,        ///< getaddrinfo: <gai_*>
+    ERR_RESOLVE_GETADDRINFO_EMPTY,  ///< getaddrinfo: no results
+};
+
+const struct error_list resolve_errors;
+
+/**
+ * Lookup result state
+ */
+struct resolve_result {
+    /** Head of the addrinfo list */
+    struct addrinfo *list;
+    
+    /** Current addrinfo item */
+    struct addrinfo *item;
+};
+
+/**
+ * Resolve the given node/service tuple as a series of addrinfos for the given socktype.
+ *
+ * This will never return an empty result.
+ *
+ * XXX: still blocking DNS stuff
+ *
+ * @param res where to store the result state
+ * @param node hostname/address to look up
+ * @param service service/port to look up
+ * @param socktype a SOCK_* value to return addrinfo's for that socktype
+ * @param ai_flags optional bitmask of AI_* flags to use
+ * @param err returned error info
+ */
+err_t resolve_addr (struct resolve_result *res, const char *node, const char *service, int socktype, int ai_flags, error_t *err);
+
+/**
+ * Initialize the given result to zero
+ */
+void resolve_result_init (struct resolve_result *res);
+
+/**
+ * Get the next address from a result, if any left
+ */
+struct addrinfo* resolve_result_next (struct resolve_result *res);
+
+/**
+ * Release the addrinfo resources associated with the given result
+ */
+void resolve_result_deinit (struct resolve_result *res);
+
+/**
+ * Returns a pointer to a static buffer containing a string description of the given addrinfo
+ */
+const char * resolve_addr_text (const struct addrinfo *addr);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/service.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,37 @@
+#include "service_internal.h"
+
+#include <stdlib.h>
+
+const struct object_type service_type_type = {
+    .parent = NULL,
+};
+
+void service_init (service_t *service, const struct service_type *type, const struct service_info *info)
+{
+    // init object
+    object_init(&service->base_obj, &type->base_type);
+
+    // store user info
+    service->info = *info;
+}
+
+void* service_check (service_t *service, const struct service_type *type)
+{
+    return object_cast(&service->base_obj, &type->base_type);
+}
+
+void service_error (service_t *service, const error_t *err)
+{
+    // just call the user callback
+    service->info.cb_tbl->on_error(service, err, service->info.cb_arg);
+}
+
+void service_destroy (service_t *service)
+{
+    const struct service_type *type = object_type(&service->base_obj, &service_type_type);
+
+    // invoke method
+    type->methods.deinit(service);
+
+    free(service); 
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/service.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,50 @@
+#ifndef LIBQMSK_SERVICE_H
+#define LIBQMSK_SERVICE_H
+
+/**
+ * @file
+ *
+ * Defines a simple interface for creating services, which listen for connections and create transport_t's.
+ */
+#include "transport.h"
+
+/**
+ * Opaque state struct.
+ */
+typedef struct service service_t;
+
+/**
+ * User callbacks for services.
+ */
+struct service_callbacks {
+    /**
+     * The service broke.
+     *
+     * This is only called for errors which occur when called directly from the event loop, and never for errors that
+     * occur inside of calls to service_*.
+     */
+    void (*on_error) (service_t *service, const error_t *err, void *arg);
+};
+
+/**
+ * User info required to build a service
+ */
+struct service_info {
+    /** Callback table */
+    const struct service_callbacks *cb_tbl;
+
+    /** Callback context arg */
+    void *cb_arg;
+
+    /** Settings for the service's client transports */
+    struct transport_info trans_info;
+};
+
+/**
+ * Destroy a service to stop accepting any connections and release all resources.
+ *
+ * Any connected client transports should stay intact (?)
+ */
+void service_destroy (service_t *service);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/service_internal.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,60 @@
+#ifndef SERVICE_INTERNAL_H
+#define SERVICE_INTERNAL_H
+
+/**
+ * @file
+ *
+ * Internal interface for implementations of service_t
+ */
+#include "service.h"
+#include "transport.h"
+#include "object.h"
+
+/**
+ * The object_type of service_type
+ */
+extern const struct object_type service_type_type;
+
+/**
+ * Type definition with method table
+ */
+struct service_type {
+    struct object_type base_type;
+    
+    /** Method table */
+    struct service_methods {
+        /**
+         * Release internal state, but not the service_t itself
+         */
+        void (*deinit) (service_t *service);
+    } methods;
+};
+
+/**
+ * Base service_t state
+ */
+struct service {
+    struct object base_obj;
+
+    /** User info */
+    struct service_info info;
+};
+
+/**
+ * Initialize a service by binding it to a specific type, with the given user info for this service, and for spawned transports.
+ */
+void service_init (service_t *service, const struct service_type *type, const struct service_info *info);
+
+/**
+ * Used to up-cast a generic service_t pointer to an implementation of the given service_type (or subtype).
+ *
+ * It is a bug to call this with a service of a different type.
+ */
+void* service_check (service_t *service, const struct service_type *type);
+
+/**
+ * The service failed, call the user callback
+ */
+void service_error (service_t *service, const error_t *err);
+
+#endif /* SERVICE_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/sock.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,22 @@
+
+#include "sock_internal.h"
+#include "ssl_internal.h"
+
+#include <assert.h>
+
+// global sock_stream_ctx instance
+struct sock_stream_ctx _sock_stream_ctx;
+
+err_t sock_init (struct event_base *ev_base, error_t *err)
+{
+    // store ev_base
+    _sock_stream_ctx.ev_base = ev_base;
+
+    // XXX: just call these all directly for now
+    if (ssl_global_init(err))
+        return error_code(err);
+
+    // done
+    return SUCCESS;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/sock.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,25 @@
+#ifndef LIBQMSK_SOCK_H
+#define LIBQMSK_SOCK_H
+
+/**
+ * @file
+ *
+ * Legacy sock_* interface for global state.
+ *
+ * XXX: replace with separate event_base per transport
+ */
+#include "error.h"
+#include <sys/types.h>
+#include <event2/event.h>
+
+/**
+ * Initialize the socket module's global state. Call this before calling any other sock_* functions.
+ *
+ * The given \a ev_base is the libevent base to use for nonblocking operation.
+ *
+ * @param ev_base the libevent base to use for events
+ * @param err returned error info
+ */
+err_t sock_init (struct event_base *ev_base, error_t *err);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/sock_internal.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,20 @@
+#ifndef SOCK_INTERNAL_H
+#define SOCK_INTERNAL_H
+
+/**
+ * @file
+ *
+ * internal sock_* interface
+ */
+#include "sock.h"
+
+/**
+ * Global sock_stream_ctx used for sock_init() and all sock_stream's
+ */
+extern struct sock_stream_ctx {
+    /** libevent core */
+    struct event_base *ev_base;
+
+} _sock_stream_ctx;
+
+#endif /* SOCK_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ssl.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,129 @@
+#include "ssl_internal.h"
+
+#include <assert.h>
+
+static const char* _gnutls_error_msg (const struct error_extra_type *type, const union error_extra *extra)
+{
+    (void) type;
+
+    return gnutls_strerror(extra->int_);
+}
+
+static const struct error_extra_type _gnutls_error_type = {
+    .name       = "gnutls",
+    .msg_func   = _gnutls_error_msg
+};
+
+const struct error_list ssl_errors = ERROR_LIST("gnutls",
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_CERT_ALLOC_CRED,         "gnutls_certificate_allocate_credentials",  &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_GLOBAL_INIT,             "gnutls_global_init",                       &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_SET_DEFAULT_PRIORITY,    "gnutls_set_default_priority",              &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_CRED_SET,                "gnutls_credentials_set",                   &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_HANDSHAKE,               "gnutls_handshake",                         &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_RECORD_SEND,             "gnutls_record_send",                       &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_RECORD_RECV,             "gnutls_record_recv",                       &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_RECORD_GET_DIRECTION,    "gnutls_record_get_direction",              &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_CERT_VERIFY_PEERS2,      "gnutls_certificate_verify_peers2",         &_gnutls_error_type    ),
+    ERROR_TYPE_STRING(  ERR_GNUTLS_CERT_VERIFY,             "X.509 Certificate verification failed"                            ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,"gnutls_certificate_set_x509_trust_file",   &_gnutls_error_type    ),
+    ERROR_TYPE_EXTRA(   ERR_GNUTLS_CERT_SET_X509_KEY_FILE,  "gnutls_certificate_set_x509_key_file",     &_gnutls_error_type    )
+);
+
+/*
+ * Global shared anonymous client credentials
+ */
+struct ssl_client_cred ssl_client_cred_anon = { .x509 = NULL, .verify = false, .refcount = 0 };
+
+
+// XXX: GnuTLS log func
+void _log (int level, const char *msg)
+{
+    printf("gnutls: %d: %s", level, msg);
+}
+
+err_t ssl_global_init (error_t *err)
+{
+    // global init
+    if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
+        return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
+
+    // initialize the anon client credentials
+    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&ssl_client_cred_anon.x509)) < 0)
+        return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
+
+    // XXX: debug
+//    gnutls_global_set_log_function(&_log);
+//    gnutls_global_set_log_level(11);
+
+    // done
+    return SUCCESS;
+}
+
+static void ssl_client_cred_destroy (struct ssl_client_cred *cred)
+{
+    // simple
+    gnutls_certificate_free_credentials(cred->x509);
+
+    free(cred);
+}
+
+err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
+        const char *cafile_path, bool verify,
+        const char *cert_path, const char *pkey_path,
+        error_t *err
+) {
+    struct ssl_client_cred *cred;
+
+    // alloc it
+    if ((cred = calloc(1, sizeof(*cred))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // create the cert
+    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&cred->x509)) < 0)
+        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
+    
+    // load the trusted ca certs?
+    if (cafile_path) {
+        // load them
+        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_trust_file(cred->x509, cafile_path, GNUTLS_X509_FMT_PEM)) < 0)
+            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_TRUST_FILE);
+
+    }
+
+    // set the verify flags?
+    cred->verify = verify;
+    gnutls_certificate_set_verify_flags(cred->x509, 0);
+
+    // load the client cert?
+    if (cert_path || pkey_path) {
+        // need both...
+        assert(cert_path && pkey_path);
+
+        // load
+        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_key_file(cred->x509, cert_path, pkey_path, GNUTLS_X509_FMT_PEM)))
+            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_KEY_FILE);
+    }
+
+    // ok
+    cred->refcount = 1;
+    *ctx_cred = cred;
+
+    return SUCCESS;
+
+error:
+    // release
+    ssl_client_cred_destroy(cred);
+
+    return ERROR_CODE(err);
+}
+
+void ssl_client_cred_get (struct ssl_client_cred *cred)
+{
+    cred->refcount++;
+}
+
+void ssl_client_cred_put (struct ssl_client_cred *cred)
+{
+    if (--cred->refcount == 0)
+        ssl_client_cred_destroy(cred);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ssl.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,75 @@
+#ifndef LIBQMSK_SSL_H
+#define LIBQMSK_SSL_H
+
+/**
+ * @file
+ *
+ * SSL transport implementation.
+ */
+#include "transport.h"
+#include <stdbool.h>
+
+/**
+ * SSL client credentials for use with ssl_client_credentials/sock_ssl_connect
+ */
+struct ssl_client_cred;
+
+/**
+ * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
+ * ourselves to the server, as well as to verify the server.
+ *
+ * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
+ * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
+ * warn. XXX: not entirely true
+ *
+ * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
+ * anonymous client certificate will be used. Both must be supplied if given.
+ *
+ * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
+ *
+ * @param ctx_cred the newly created client credentials are returned via this
+ * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
+ * @param verify force verification of the peer cert
+ * @param cert_path path to the client certificate file, or NULL
+ * @param pkey_path path to the client private key, or NULL
+ * @param err returned error info
+ */
+err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
+        const char *cafile_path, bool verify,
+        const char *cert_path, const char *pkey_path,
+        error_t *err
+);
+
+/**
+ * Aquire a referenec for the given cred.
+ */
+void ssl_client_cred_get (struct ssl_client_cred *cred);
+
+/**
+ * Release a reference allocated for the given cred.
+ */
+void ssl_client_cred_put (struct ssl_client_cred *cred);
+
+/**
+ * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
+ * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
+ * the given \a cb_func.
+ *
+ * The given ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
+ * or a ssl_client_cred allocated using ssl_client_cred_create(). The contexts are reference-counted, so once
+ * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
+ * 
+ * @param info the transport setup info
+ * @param transport_ptr returned transport
+ * @param hostname the hostname to connect to
+ * @param service the TCP service name (i.e. port) to connect to
+ * @param cred the SSL client credentials to use, if not NULL
+ * @param err returned error info
+ */
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *hostname, const char *service,
+        struct ssl_client_cred *cred,
+        error_t *err
+    );
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ssl_client.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,454 @@
+#include "ssl_internal.h"
+
+#include <gnutls/x509.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+// XXX: remove
+#include "log.h"
+#include <assert.h>
+
+
+/**
+ * Cast a ssl_client to a sock_fd.
+ */
+#define SSL_CLIENT_FD(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd)
+
+/**
+ * Cast a ssl_client to a sock_stream.
+ */
+#define SSL_CLIENT_TRANSPORT(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd.base)
+
+
+
+/**
+ * Enable the TCP events based on the session's gnutls_record_get_direction().
+ */
+static err_t ssl_client_ev_enable (struct ssl_client *client, error_t *err)
+{
+    int ret;
+    short mask;
+
+    // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
+    // operation, so we can use that to determine what events to register
+    switch ((ret = gnutls_record_get_direction(client->session))) {
+        case 0: 
+            // read more data
+            mask = TRANSPORT_READ;
+            break;
+        
+        case 1:
+            // write buffer full
+            mask = TRANSPORT_WRITE;
+            break;
+        
+        default:
+            // random error
+            RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
+    }
+    
+    // do the enabling
+    if ((ERROR_CODE(err) = transport_fd_enable(SSL_CLIENT_FD(client), mask)))
+        return ERROR_CODE(err);
+    
+
+    return SUCCESS;
+}
+
+/**
+ * Translate a set of gnutls_certificate_status_t values to a constant error message
+ */
+static const char* ssl_client_verify_error (unsigned int status)
+{
+    if (status & GNUTLS_CERT_REVOKED)
+        return "certificate was revoked";
+
+    else if (status & GNUTLS_CERT_INVALID) {
+        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+            return "certificate signer was not found";
+
+        else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
+            return "certificate signer is not a Certificate Authority";
+
+        else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+            return "certificate signed using an insecure algorithm";
+
+        else
+            return "certificate could not be verified";
+
+    } else
+        return "unknown error";
+
+}
+
+/**
+ * Perform the certificate validation procedure on the peer cert.
+ *
+ * Based on the GnuTLS examples/ex-rfc2818.c
+ */
+static err_t ssl_client_verify (struct ssl_client *client, error_t *err)
+{
+    unsigned int status;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size;
+    gnutls_x509_crt_t cert = NULL;
+    time_t t, now;
+
+    // init
+    RESET_ERROR(err);
+    now = time(NULL);
+    
+    // inspect the peer's cert chain using the installed trusted CAs
+    if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(client->session, &status)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
+
+    // verify errors?
+    if (status)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, ssl_client_verify_error(status));
+    
+    // import the main cert
+    assert(gnutls_certificate_type_get(client->session) == GNUTLS_CRT_X509);
+
+    if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
+
+    if ((cert_list = gnutls_certificate_get_peers(client->session, &cert_list_size)) == NULL)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
+
+    if (!cert_list_size)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
+
+    if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
+    
+    // check expire/activate... not sure if we need to do this
+    if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
+
+    if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
+    
+    // check hostname
+    if (!gnutls_x509_crt_check_hostname(cert, client->hostname))
+        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
+
+error:
+    // cleanup
+    if (cert)
+        gnutls_x509_crt_deinit(cert);
+    
+    // should be SUCCESS
+    return ERROR_CODE(err);    
+}
+
+
+/**
+ * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
+ *
+ * This updates the ssl_client::handshake state internally, as used by ssl_client_event_handler.
+ *
+ * If the client is marked as verify, this will perform the verification, returning on any errors, and then unset the
+ * verify flag - this ensures that the peer cert is only verified once per connection...
+ *
+ * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
+ */
+static int ssl_client_handshake (struct ssl_client *client, error_t *err)
+{
+    int ret;
+
+    // perform the handshake
+    if ((ret = gnutls_handshake(client->session)) < 0 && ret != GNUTLS_E_AGAIN)
+        JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
+    
+    // complete?
+    if (ret == 0) {
+        // update state
+        client->handshake = false;
+
+        // verify?
+        if (client->verify) {
+            // perform the validation
+            if (ssl_client_verify(client, err))
+                goto error;
+            
+            // unmark
+            client->verify = false;
+        }
+
+        // handshake done
+        return 1;
+
+    } else {
+        // set state, isn't really needed every time, but easier this way
+        client->handshake = true;
+
+        // re-enable the event for the next iteration
+        return ssl_client_ev_enable(client, err);
+    }
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+/**
+ * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
+ */
+static void ssl_client_on_event (struct transport_fd *fd, short what, void *arg)
+{
+    struct ssl_client *client = arg;
+    error_t err;
+
+    (void) fd;
+
+    // XXX: timeouts
+    (void) what;
+
+    // are we in the handshake cycle?
+    if (client->handshake) {
+        RESET_ERROR(&err);
+
+        // perform the next handshake step
+        // this returns zero when the handshake is not yet done, errors/completion then trigger the else-if-else below
+        if (ssl_client_handshake(client, &err) == 0) {
+            // handshake continues
+        
+        } else if (!SSL_CLIENT_TRANSPORT(client)->connected) {
+            // the async connect+handshake process has completed
+            // invoke the user connect callback directly with appropriate error
+            transport_connected(SSL_CLIENT_TRANSPORT(client), ERROR_CODE(&err) ? &err : NULL, true);
+
+        } else {
+            // in-connection re-handshake completed
+            if (ERROR_CODE(&err))
+                // the re-handshake failed, so this transport is dead
+                transport_error(SSL_CLIENT_TRANSPORT(client), &err);
+        
+            else
+                // re-handshake completed, so continue with the transport_callbacks
+                transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+        }
+
+    } else {
+        // normal transport operation
+        // gnutls might be able to proceed now, so invoke user callbacks
+        transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+    }
+}
+
+static err_t ssl_client__read (transport_t *transport, void *buf, size_t *len, error_t *err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    int ret;
+    
+    // read gnutls record
+    do {
+        ret = gnutls_record_recv(client->session, buf, *len);
+
+    } while (ret == GNUTLS_E_INTERRUPTED);
+    
+    // errors
+    // XXX: E_REHANDSHAKE?
+    if (ret < 0 && ret != GNUTLS_E_AGAIN)
+        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
+    
+    else if (ret == 0)
+        return SET_ERROR(err, ERR_EOF);
+
+
+    // EAGAIN?
+    if (ret < 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+
+    }
+
+    return SUCCESS;
+}
+
+static err_t ssl_client__write (transport_t *transport, const void *buf, size_t *len, error_t *err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    int ret;
+ 
+    // read gnutls record
+    do {
+        ret = gnutls_record_send(client->session, buf, *len);
+   
+    } while (ret == GNUTLS_E_INTERRUPTED);
+
+    // errors
+    if (ret < 0 && ret != GNUTLS_E_AGAIN)
+        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_SEND, ret);
+    
+    else if (ret == 0)
+        return SET_ERROR(err, ERR_WRITE_EOF);
+
+
+    // eagain?
+    if (ret < 0) {
+        *len = 0;
+
+    } else {
+        // updated length
+        *len = ret;
+    }
+
+    return SUCCESS;
+}
+
+void ssl_client_deinit (struct ssl_client *client)
+{
+    // close the session rudely
+    gnutls_deinit(client->session);
+    client->session = NULL;
+ 
+    // terminate the TCP transport
+    tcp_client_deinit(&client->base_tcp);
+   
+    if (client->cred) {
+        // drop the cred ref
+        ssl_client_cred_put(client->cred);
+
+        client->cred = NULL;
+    }
+
+    // free
+    free(client->hostname);
+    client->hostname = NULL;
+}
+
+
+static void ssl_client__deinit (transport_t *transport)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    
+    // die
+    ssl_client_deinit(client);
+}
+
+/**
+ * Our tcp_client-invoked connect handler
+ */
+static void ssl_client__connected (transport_t *transport, const error_t *tcp_err)
+{
+    struct ssl_client *client = transport_check(transport, &ssl_client_type);
+    error_t err;
+
+    // trap errors to let the user handle them directly
+    if (tcp_err)
+        JUMP_SET_ERROR_INFO(&err, tcp_err);
+    
+    // bind default transport functions (recv/send) to use the TCP fd
+    gnutls_transport_set_ptr(client->session, (gnutls_transport_ptr_t) (long int) SSL_CLIENT_FD(client)->fd);
+
+    // add ourselves as the event handler
+    if ((ERROR_CODE(&err) = transport_fd_setup(SSL_CLIENT_FD(client), ssl_client_on_event, client)))
+        goto error;
+
+    // start handshake
+    if (ssl_client_handshake(client, &err))
+        // this should complete with SUCCESS if it returns >0
+        goto error;
+
+    // ok, so we wait...
+    return;
+
+error:
+    // tell the user
+    transport_connected(transport, &err, true);
+}
+
+struct transport_type ssl_client_type = {
+    .base_type = {
+        .parent     = &tcp_client_type.base_type,
+    },
+    .methods = {
+        .read       = ssl_client__read,
+        .write      = ssl_client__write,
+        .deinit     = ssl_client__deinit,
+        ._connected = ssl_client__connected,
+    },
+};
+
+
+
+static void ssl_client_destroy (struct ssl_client *client)
+{
+    ssl_client_deinit(client);
+
+    free(client);
+}
+
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *hostname, const char *service,
+        struct ssl_client_cred *cred,
+        error_t *err
+    )
+{
+    struct ssl_client *client = NULL;
+
+    // alloc
+    if ((client = calloc(1, sizeof(*client))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // initialize base
+    transport_init(SSL_CLIENT_TRANSPORT(client), &ssl_client_type, info);
+
+    if (!cred) {
+        // default credentials
+        cred = &ssl_client_cred_anon;
+    
+    } else {
+        // take a ref
+        client->cred = cred;
+        cred->refcount++;
+    };
+
+    // do verify?
+    if (cred->verify)
+        client->verify = true;
+
+    // init
+    if ((client->hostname = strdup(hostname)) == NULL)
+        JUMP_SET_ERROR(err, ERR_STRDUP);
+
+    // initialize TCP
+    tcp_client_init(&client->base_tcp);
+
+    // initialize client session
+    if ((ERROR_EXTRA(err) = gnutls_init(&client->session, GNUTLS_CLIENT)) < 0)
+        JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
+
+    // ...default priority stuff
+    if ((ERROR_EXTRA(err) = gnutls_set_default_priority(client->session)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
+
+    // XXX: silly hack for OpenSSL interop
+    gnutls_dh_set_prime_bits(client->session, 512);
+
+    // bind credentials
+    if ((ERROR_EXTRA(err) = gnutls_credentials_set(client->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
+        JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
+
+    // TCP connect
+    if (tcp_client_connect_async(&client->base_tcp, hostname, service, err))
+        goto error;
+
+    // done, wait for the connect to complete
+    *transport_ptr = SSL_CLIENT_TRANSPORT(client);
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    ssl_client_destroy(client);
+
+    return ERROR_CODE(err);    
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ssl_internal.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,88 @@
+#ifndef LIBQMSK_SSL_INTERNAL_H
+#define LIBQMSK_SSL_INTERNAL_H
+
+/**
+ * @file
+ *
+ * A sock_stream implementation using GnuTLS for SSL
+ */
+#include "ssl.h"
+#include "tcp_internal.h"
+
+#include <gnutls/gnutls.h>
+
+/**
+ * GnuTLS library error codes
+ */
+enum ssl_error_code {
+    ERR_GNUTLS_NONE, 
+    ERR_GNUTLS_CERT_ALLOC_CRED,
+    ERR_GNUTLS_GLOBAL_INIT,
+    ERR_GNUTLS_INIT,
+    ERR_GNUTLS_SET_DEFAULT_PRIORITY,
+    ERR_GNUTLS_CRED_SET,
+    ERR_GNUTLS_HANDSHAKE,
+    ERR_GNUTLS_RECORD_SEND,
+    ERR_GNUTLS_RECORD_RECV,
+    ERR_GNUTLS_RECORD_GET_DIRECTION,   
+    ERR_GNUTLS_CERT_VERIFY_PEERS2,
+    ERR_GNUTLS_CERT_VERIFY,
+    ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
+    ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
+};
+
+const struct error_list ssl_errors;
+
+/**
+ * GnuTLS credentials for client sockets.
+ */
+struct ssl_client_cred {
+    /** Our client certificate */
+    gnutls_certificate_credentials_t x509;
+
+    /** Should we verify? */
+    bool verify;
+
+    /** Refcount from ssl_client */
+    int refcount;
+};
+
+/**
+ * Global anonymous x509 credentials
+ */
+extern struct ssl_client_cred ssl_client_cred_anon;
+
+/*
+ * Our transport_type
+ */
+extern struct transport_type ssl_client_type;
+
+/**
+ * An SSL-encrypted TCP connection, using libgnutls
+ */
+struct ssl_client {
+    /** The underlying TCP connection */
+    struct tcp_client base_tcp;
+
+    /** The hostname we connected to, for verification */
+    char *hostname;
+
+    /** The credentials we are using, unless anon */
+    struct ssl_client_cred *cred;
+    
+    /** The GnuTLS session for this connection */
+    gnutls_session_t session;
+
+    /** Should we verify the peer cert? */
+    bool verify;
+
+    /** Are we running a handshake? */
+    bool handshake;
+};
+
+/**
+ * Initialize the global gnutls state
+ */
+err_t ssl_global_init (error_t *err);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,43 @@
+#include "tcp_internal.h"
+
+int tcp_sock_create (const struct addrinfo *addr, error_t *err)
+{
+    int sock;
+
+    // create a new socket using addr->ai_family/socktype/protocol
+    if ((sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
+        JUMP_SET_ERROR_ERRNO(err, ERR_SOCKET);
+
+    return sock;
+
+error:
+    return -ERROR_CODE(err);    
+}
+
+err_t tcp_sock_error (evutil_socket_t sock, error_t *err)
+{
+    int optval;
+    socklen_t optlen;
+
+    RESET_ERROR(err);
+
+    // init params
+    optval = 0;
+    optlen = sizeof(optval);
+
+    // read error code
+    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen))
+        RETURN_SET_ERROR_ERRNO(err, ERR_GETSOCKOPT);
+    
+    // sanity-check optlen... not sure if this is sensible
+    if (optlen != sizeof(optval))
+        RETURN_SET_ERROR_EXTRA(err, ERR_GETSOCKOPT, EINVAL);
+    
+    // then store the system error code
+    ERROR_EXTRA(err) = optval;
+
+    // ok
+    return SUCCESS;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,35 @@
+#ifndef LIBQMSK_TCP_H
+#define LIBQMSK_TCP_H
+
+/**
+ * @file
+ *
+ * TCP transport/service implementation.
+ *
+ * XXX: provide some TCP-specific type/functions?
+ */
+#include "transport.h"
+#include "service.h"
+
+/**
+ * Connect the given transport via TCP to the given host/service. The transport will not be ready for use until the
+ * transport_callbacks::on_connect callback has been invoked.
+ *
+ * XXX: blocking DNS resolution
+ *
+ * @param info the transport setup info
+ * @param transport_ptr returned transport
+ * @param host the hostname to connect to
+ * @param service the service name (i.e. port) to connect to
+ * @param err returned error info
+ */
+err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *host, const char *service, error_t *err);
+
+/**
+ * Create a passive/listening TCP socket on the given interface/port (NULL to pick automatically).
+ */
+err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
+        const char *interface, const char *service, error_t *err);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp_client.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,278 @@
+#include "tcp_internal.h"
+#include "log.h"
+
+/*
+ * Our transport methods
+ */
+static void tcp_client__deinit (transport_t *transport)
+{
+    struct tcp_client *client = transport_check(transport, &tcp_client_type);
+    
+    // proxy
+    tcp_client_deinit(client);
+}
+
+/*
+ * Our transport_type
+ */
+const struct transport_type tcp_client_type = {
+    .base_type = {
+        .parent             = &tcp_transport_type.base_type,
+    },
+    .methods                = {
+        .read               = transport_fd__read,
+        .write              = transport_fd__write,
+        .events             = transport_fd__events,
+        .deinit             = tcp_client__deinit,
+    },
+};
+
+/*
+ * Forward-declare 
+ */
+static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg);
+
+/*
+ * Function implementations
+ */
+void tcp_client_init (struct tcp_client *client)
+{
+    tcp_transport_init(&client->base_trans, -1);
+    
+    resolve_result_init(&client->rr);
+}
+
+/*
+ * Start connecting to the given address in a non-blocking fashion. Returns any errors that immediately crop up,
+ * otherwise eventually calls tcp_client_connect_done().
+ */
+static err_t tcp_client_connect_addr (struct tcp_client *client, struct addrinfo *addr, error_t *err)
+{
+    struct transport_fd *_fd = &client->base_trans.base_fd;
+    int ret;
+    evutil_socket_t sock;
+    err_t tmp;
+
+    // first, create the socket
+    if ((sock = tcp_sock_create(addr, err)) < 0)
+        return ERROR_CODE(err);
+
+    // set it as our sock
+    if ((ERROR_CODE(err) = transport_fd_set(_fd, sock)))
+        goto error;
+
+    // then, set it up as nonblocking
+    if ((ERROR_CODE(err) = transport_fd_nonblock(_fd, true)))
+        goto error;
+
+    // then, initiate the connect
+    if ((ret = connect(sock, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS) 
+        JUMP_SET_ERROR_ERRNO(err, ERR_CONNECT);
+    
+    if (ret < 0) {
+        // ok, connect started, setup our completion callback
+        if ((ERROR_CODE(err) = transport_fd_setup(_fd, tcp_client_on_connect, client)))
+            goto error;
+    
+        // enable for write
+        if ((ERROR_CODE(err) = transport_fd_enable(_fd, TRANSPORT_WRITE)))
+            goto error;
+
+    } else {
+        // oops... blocking connect - fail to avoid confusion
+        // XXX: come up with a better error name to use
+        // XXX: support non-async connects as well
+        JUMP_SET_ERROR_EXTRA(err, ERR_CONNECT, EINPROGRESS);
+    }
+    
+    // ok
+    return SUCCESS;
+
+error:
+    // close the stuff we did open
+    if ((tmp = transport_fd_close(_fd)))
+        log_warn("error closing socket after connect error: %s", error_name(tmp));
+
+    return ERROR_CODE(err);
+}
+
+
+/*
+ * Attempt to connect to the next addrinfo, or the next one, if that fails, etc.
+ *
+ * This does not call transport_connected().
+ */
+static err_t tcp_client_connect_continue (struct tcp_client *client, error_t *err)
+{
+    struct addrinfo *addr;
+
+    // try and connect to each one until we find one that works
+    while ((addr = resolve_result_next(&client->rr))) {
+        // attempt to start connect
+        if (tcp_client_connect_addr(client, addr, err) == SUCCESS)
+            break;
+
+        // log a warning on the failed connect
+        log_warn_error(err, "%s", resolve_addr_text(addr));
+    }
+    
+
+    if (addr)
+        // we succesfully did a tcp_client_connect_addr on valid address
+        return SUCCESS;
+
+    else
+        // all of the connect_async_addr's failed, return the last error
+        return ERROR_CODE(err);
+}
+
+/*
+ * Cleanup our resolver state and any connect callbacks after a connect
+ */
+static void tcp_client_connect_cleanup (struct tcp_client *client)
+{
+    // drop the resolver stuff
+    resolve_result_deinit(&client->rr);
+    
+    // remove our event handler
+    transport_fd_clear(&client->base_trans.base_fd);
+}
+
+/*
+ * Our async connect operation has completed, clean up, set up state for event-based operation with user callbacks, and
+ * invoke transport_connected().
+ *
+ * The given \a err should be NULL for successful completion, or the error for failures.
+ */
+static void tcp_client_connect_done (struct tcp_client *client, error_t *conn_err)
+{
+    error_t err;
+
+    // cleanup
+    tcp_client_connect_cleanup(client);
+
+    if (conn_err)
+        JUMP_SET_ERROR_INFO(&err, conn_err);
+
+    // let the transport handle the rest
+    if (tcp_transport_connected(&client->base_trans, &err))
+        goto error;
+    
+    // ok
+    return;
+
+error:    
+    // pass the error on to transport
+    transport_connected(&client->base_trans.base_fd.base, &err, false);
+}
+
+/*
+ * Our async connect callback
+ */
+static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg)
+{
+    struct tcp_client *client = arg;
+    error_t err;
+    err_t tmp;
+
+    // XXX: timeouts
+    (void) what;
+    
+    // read socket error code
+    if (tcp_sock_error(client->base_trans.base_fd.fd, &err))
+        goto error;
+
+    // did the connect fail?
+    if (ERROR_EXTRA(&err))
+        JUMP_SET_ERROR(&err, ERR_CONNECT);
+    
+    // done, success
+    return tcp_client_connect_done(client, NULL);
+
+error:
+    // close the socket
+    if ((tmp = transport_fd_close(fd)))
+        log_warn("error closing socket after connect error: %s", error_name(tmp));
+
+    // log a warning
+    log_warn_error(&err, "connect to %s failed", "???");
+
+    // try the next one or fail completely
+    if (tcp_client_connect_continue(client, &err))
+        tcp_client_connect_done(client, &err);
+}
+
+err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err)
+{
+    // do the resolving
+    if (resolve_addr(&client->rr, hostname, service, SOCK_STREAM, 0, err))
+        return ERROR_CODE(err);
+
+    // start connecting with the first result
+    if (tcp_client_connect_continue(client, err))
+        goto error;
+
+    // ok
+    return SUCCESS;
+
+error:
+    // cleanup
+    resolve_result_deinit(&client->rr);
+    
+    return ERROR_CODE(err);
+}
+
+void tcp_client_deinit (struct tcp_client *client)
+{
+    // cleanup our stuff
+    resolve_result_deinit(&client->rr);
+    
+    // deinit lower transport
+    tcp_transport_deinit(&client->base_trans);
+}
+
+/*
+ * Deinit and free, not using the transport interface
+ */
+static void tcp_client_destroy (struct tcp_client *client)
+{
+    tcp_client_deinit(client);
+
+    free(client);
+}
+
+/*
+ * Public interface
+ */
+err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
+        const char *host, const char *service, error_t *err)
+{
+    struct tcp_client *client;
+ 
+    // alloc
+    if ((client = calloc(1, sizeof(*client))) == NULL)
+        return ERR_CALLOC;
+
+    // init transport
+    transport_init(&client->base_trans.base_fd.base, &tcp_client_type, info);
+    
+    // init our state
+    tcp_client_init(client);
+ 
+    // begin connect
+    if (tcp_client_connect_async(client, host, service, err))
+        goto error;
+
+    // good
+    *transport_ptr = &client->base_trans.base_fd.base;
+
+    return 0;
+
+error:
+    // cleanup
+    tcp_client_destroy(client);
+        
+    // return error code
+    return ERROR_CODE(err);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp_internal.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,170 @@
+#ifndef LIBQMSK_TCP_INTERNAL_H
+#define LIBQMSK_TCP_INTERNAL_H
+
+/**
+ * @file
+ *
+ * Internal TCP interface for implementations
+ */
+#include "tcp.h"
+#include "resolve.h"
+#include "transport_fd.h"
+#include "transport_internal.h"
+#include "service_internal.h"
+#include "error.h"
+
+#include <event2/event.h>
+#include <event2/util.h>
+
+/**
+ * Create a new socket() using the given addr's family/socktype/protocol and return it.
+ *
+ * In case of errors, this returns -err_t
+ *
+ * @param addr the addrinfo to create the socket for
+ * @param err returned error info
+ * @return new fd on success, -err_t on error
+ */
+int tcp_sock_create (const struct addrinfo *addr, error_t *err);
+
+/**
+ * Return the socket's current error code via err->extra.
+ *
+ * In case getting the socket error code itself fails, this will return normal error code/info.
+ *
+ * Otherwise, this will return SUCCESS, with the errno value stored in err->extra.
+ */
+err_t tcp_sock_error (evutil_socket_t sock, error_t *err);
+
+
+/**
+ * TCP transport type
+ */
+extern const struct transport_type tcp_transport_type;
+
+/**
+ * Base TCP transport 
+ *
+ * XXX: currently just the same as transport_fd, but this will probably change
+ */
+struct tcp_transport {
+    /** Base FD-based implementation */
+    struct transport_fd base_fd;
+};
+
+/**
+ * Initialize the tcp_transport state.
+ *
+ * This initializes the transport_fd base using the global sock_ctx::ev_base and the given socket.
+ */
+void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock);
+
+/**
+ * Create a new tcp_transport with the given sock.
+ *
+ * For convenience, this will also make the sock nonblocking.
+ *
+ * In case of errors, this will the socket.
+ *
+ * @param trans_ptr returned tcp_transport
+ * @param info the transport user settings
+ * @param sock the unused TCP socket
+ * @param err returned error info
+ */
+err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err);
+
+/**
+ * The transport as now connected, this sets up the intitial user settings, and invokes the callback.
+ *
+ * XXX: this does an 'indirect' call to transport_connected().
+ *
+ * @param err returned error info
+ */
+err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err);
+
+/**
+ * Deinitialize the transport state, terminating the connection and releasing resources.
+ */
+void tcp_transport_deinit (struct tcp_transport *trans);
+
+/**
+ * Deinitialize and free the given tcp_transport
+ */
+void tcp_transport_destroy (struct tcp_transport *trans);
+
+/**
+ * TCP client transport type
+ */
+extern const struct transport_type tcp_client_type;
+
+/**
+ * TCP client state
+ */
+struct tcp_client {
+    /** Base transport stuff */
+    struct tcp_transport base_trans;
+
+    /** The resolver lookup result for the async connect process */
+    struct resolve_result rr;
+};
+
+/**
+ * Initialize the tcp_client state
+ */
+void tcp_client_init (struct tcp_client *client);
+
+/**
+ * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, this will
+ * call transport_connected(), so you can register transport_methods::_connected if layering on top of TCP.
+ *
+ * In case of errors while starting the async connect process, an error code will be returned. If the connect fails
+ * later on, transport_connected() will be called with the error code.
+ *
+ * The sock must have been initialized using sock_tcp_init().
+ *
+ * @param client the unconnected TCP client socket to connect with
+ * @param hostname the hostname to resolve
+ * @param service the service to connect to
+ * @param err returned error info for immediate errors
+ */
+err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err);
+
+/**
+ * Deinitialize the tcp_client's state, including the tcp_transport state.
+ */
+void tcp_client_deinit (struct tcp_client *client);
+
+
+
+/**
+ * TCP service type
+ */
+extern const struct service_type tcp_server_type;
+
+/**
+ * TCP service state
+ */
+struct tcp_server {
+    /** Base service state */
+    struct service base_service;
+    
+    /** The input event with our listen() socket */
+    struct event *ev;
+};
+
+/**
+ * The listen() backlog
+ */
+#define TCP_SERVER_BACKLOG 5
+
+/**
+ * Open the listening socket on the given interface/service.
+ */
+err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err);
+
+/**
+ * Release the tcp_server's state, and cleanup the struct.
+ */
+void tcp_server_deinit (struct tcp_server *serv);
+
+#endif /* TCP_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp_server.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,260 @@
+#include "tcp_internal.h"
+#include "sock_internal.h"
+#include "log.h"
+
+#include <unistd.h>
+
+/*
+ * Service methods
+ */
+void tcp_server__deinit (service_t *service)
+{
+    struct tcp_server *serv = service_check(service, &tcp_server_type);
+
+    tcp_server_deinit(serv);
+}
+
+/*
+ * Service type
+ */
+const struct service_type tcp_server_type = {
+    .base_type = {
+        .parent = &service_type_type,
+    },
+    .methods = {
+        .deinit = tcp_server__deinit,
+    },
+};
+
+/*
+ * We got a new client, build a transport for it and give it to the user
+ */
+static err_t tcp_server_client (struct tcp_server *serv, evutil_socket_t sock, error_t *err)
+{
+    struct tcp_transport *trans;
+    
+    // create a new transport for it, this also makes it nonblocking
+    if (tcp_transport_create(&trans, &serv->base_service.info.trans_info, sock, err))
+        goto error;
+
+    // make it connected
+    // this will call transport_callbacks::on_connect, which is all the user needs
+    if (tcp_transport_connected(trans, err))
+        goto error;
+
+    // ok
+    return SUCCESS;
+
+error:
+    // cleanup
+    if (trans)
+        tcp_transport_destroy(trans);
+
+    return ERROR_CODE(err);    
+}
+
+/*
+ * Libevent callback
+ */
+static void tcp_server_on_accept (evutil_socket_t sock, short what, void *arg)
+{
+    struct tcp_server *serv = arg;
+    evutil_socket_t client_sock;
+    error_t err;
+
+    (void) what;
+
+    // accept as a new client connection
+    if ((client_sock = accept(sock, NULL, NULL)) < 0 && errno != EAGAIN)
+        JUMP_SET_ERROR_ERRNO(&err, ERR_ACCEPT);
+
+    // spurious read event?
+    if (client_sock < 0)
+        return;
+
+    // handle it
+    if (tcp_server_client(serv, client_sock, &err))
+        goto error;
+
+    // ok
+    return;
+
+error:
+    if (client_sock >= 0)
+        EVUTIL_CLOSESOCKET(client_sock);
+
+    // faaail
+    service_error(&serv->base_service, &err);
+}
+
+/*
+ * Attempts to construct a listen()'d socket with the given addr, and return it
+ *
+ * @param addr the addrinfo to try and create a socket for
+ * @param err returned error info
+ * @return listening socket, or -err_t on error
+ */
+static int tcp_server_sock_addr (struct addrinfo *addr, error_t *err)
+{
+    evutil_socket_t sock;
+
+    // create the sock
+    if ((sock = tcp_sock_create(addr, err)) < 0)
+        goto error;
+
+    // bind it
+    if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0)
+        JUMP_SET_ERROR_ERRNO(err, ERR_BIND);
+
+    // listen
+    if (listen(sock, TCP_SERVER_BACKLOG) < 0)
+        JUMP_SET_ERROR_ERRNO(err, ERR_LISTEN);
+    
+    // ok, valid socket
+    return sock;
+
+error:
+    if (sock >= 0)
+        // cleanup
+        EVUTIL_CLOSESOCKET(sock);
+    
+    return -ERROR_CODE(err);
+}
+
+/*
+ * Construct a listen()'d socket with the given resolver result, and return it.
+ *
+ * @param rr the resolver lookup result to create a socket for
+ * @param err returned error info
+ * @return listening socket, or -err_t on error
+ */
+static int tcp_server_sock (struct resolve_result *rr, error_t *err)
+{
+    struct addrinfo *addr;
+    evutil_socket_t sock;
+
+    // try each addrinfo
+    while ((addr = resolve_result_next(rr))) {
+        // attempt to construct given socket
+        if ((sock = tcp_server_sock_addr(addr, err)) < 0)
+            // log an informative error warning
+            log_warn_error(err, "%s", resolve_addr_text(addr)); 
+
+        else
+            // got a valid socket 
+            break;
+    }
+    
+    if (sock >= 0)
+        // valid socket
+        return sock;
+
+    else
+        // some error occured
+        return -ERROR_CODE(err);
+}
+
+err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err)
+{
+    struct resolve_result rr;
+    evutil_socket_t sock;
+
+    // get the global event_base
+    struct event_base *ev_base = _sock_stream_ctx.ev_base;
+
+    // init the resolver
+    resolve_result_init(&rr);
+
+    // resolve the interface/service
+    if (resolve_addr(&rr, interface, service, SOCK_STREAM, AI_PASSIVE, err))
+        return ERROR_CODE(err);
+    
+    // create the socket
+    if ((sock = tcp_server_sock(&rr, err)) < 0)
+        goto error;
+
+    // deinit lookup results
+    resolve_result_deinit(&rr);
+
+    // make it nonblocking
+    if (evutil_make_socket_nonblocking(sock))
+        JUMP_SET_ERROR_STR(err, ERR_MISC, "evutil_make_socket_nonblocking");
+    
+    // construct event for the sock
+    if ((serv->ev = event_new(ev_base, sock, EV_READ | EV_PERSIST, tcp_server_on_accept, serv)) == NULL)
+        JUMP_SET_ERROR(err, ERR_EVENT_NEW);
+
+    // add it
+    if (event_add(serv->ev, NULL))
+        JUMP_SET_ERROR(err, ERR_EVENT_ADD);
+
+    // ok
+    return SUCCESS;
+
+error:
+    // deinit results just to be sure
+    resolve_result_deinit(&rr);
+
+    if (sock >= 0 && !serv->ev)
+        // need to close socket ourselves, because we couldn't register our event for it
+        EVUTIL_CLOSESOCKET(sock);
+    
+    // general cleanup
+    tcp_server_deinit(serv);
+    
+    return ERROR_CODE(err);
+}
+
+void tcp_server_deinit (struct tcp_server *serv)
+{
+    if (serv->ev) {
+        // ignore errors
+        event_del(serv->ev);
+
+        // ignore errors
+        close(event_get_fd(serv->ev));
+        
+        // release event
+        event_free(serv->ev);
+        
+        // invalidate
+        serv->ev = NULL;
+    }
+}
+
+static void tcp_server_destroy (struct tcp_server *serv)
+{
+    tcp_server_deinit(serv);
+
+    free(serv);
+}
+
+/*
+ * Public interface
+ */
+err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
+        const char *interface, const char *service, error_t *err)
+{
+    struct tcp_server *serv;
+
+    // alloc
+    if ((serv = calloc(1, sizeof(*serv))) == NULL)
+        return SET_ERROR(err, ERR_MEM);
+
+    // init service
+    service_init(&serv->base_service, &tcp_server_type, info);
+
+    // init ourselves
+    if (tcp_server_listen(serv, interface, service, err))
+        goto error;
+
+    // ok
+    *service_ptr = &serv->base_service;
+
+    return SUCCESS;
+
+error:
+    tcp_server_destroy(serv);
+
+    return ERROR_CODE(err);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/tcp_transport.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,86 @@
+#include "tcp_internal.h"
+#include "sock_internal.h"
+
+#include <unistd.h>
+
+/*
+ * Our transport_type
+ */
+const struct transport_type tcp_transport_type = {
+    .base_type = {
+        .parent             = &transport_fd_type.base_type,
+    },
+    .methods                = {
+        .read               = transport_fd__read,
+        .write              = transport_fd__write,
+        .events             = transport_fd__events,
+        .deinit             = transport_fd__deinit,
+    },
+};
+
+void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock)
+{
+    struct event_base *ev_base = _sock_stream_ctx.ev_base;
+
+    transport_fd_init(&trans->base_fd, ev_base, sock);
+}
+
+err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err)
+{
+    struct tcp_transport *trans;
+
+    // alloc
+    if ((trans = calloc(1, sizeof(*trans))) == NULL)
+        JUMP_SET_ERROR(err, ERR_MEM);
+
+    // init transport
+    transport_init(&trans->base_fd.base, &tcp_transport_type, info);
+
+    // init ourselves
+    tcp_transport_init(trans, sock);
+
+    // setup the socket?
+    if (sock >= 0) {
+        // make it non-blocking
+        if ((ERROR_CODE(err) = transport_fd_nonblock(&trans->base_fd, true)))
+            goto error;
+    }
+
+    // ok
+    *trans_ptr = trans;
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    if (trans)
+        tcp_transport_deinit(trans);
+    else
+        EVUTIL_CLOSESOCKET(sock);
+
+    return ERROR_CODE(err);
+}
+
+err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err)
+{
+    // set up for default transport event-based operation
+    if ((ERROR_CODE(err) = transport_fd_defaults(&trans->base_fd)))
+        return ERROR_CODE(err);
+
+    // ok
+    transport_connected(&trans->base_fd.base, NULL, false);
+
+    return SUCCESS;
+}
+
+void tcp_transport_deinit (struct tcp_transport *trans)
+{
+    transport_fd_deinit(&trans->base_fd);
+}
+
+void tcp_transport_destroy (struct tcp_transport *trans)
+{
+    tcp_transport_deinit(trans);
+
+    free(trans);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,160 @@
+#include "transport_internal.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+const struct error_list transport_errors = ERROR_LIST("transport",
+    ERROR_TYPE(         ERR_TRANSPORT_EOF,              "EOF"                       ),
+    ERROR_TYPE(         ERR_TRANSPORT_READABLE,         "transport not readable"    ),
+    ERROR_TYPE(         ERR_TRANSPORT_WRITEABLE,        "transport not writeable"   )
+);
+
+/**
+ * Our own object_type
+ */
+const struct object_type transport_type_type = {
+    .parent     = NULL,
+};
+
+/*
+ * Internal API
+ */
+void transport_init (transport_t *transport, const struct transport_type *type, const struct transport_info *info)
+{
+    // init object
+    object_init(&transport->base_obj, &type->base_type);
+    
+    // store
+    if (info)
+        transport->info = *info;
+}
+
+void* transport_check (transport_t *transport, const struct transport_type *type)
+{
+    // trip as a bug
+    assert(object_check(&transport->base_obj, &type->base_type));
+
+    // ok, cast via void*
+    return transport;
+}
+
+void transport_connected (transport_t *transport, const error_t *err, bool direct)
+{
+    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
+
+    if (direct || !type->methods._connected) {
+        // user callback
+        if (err) {
+            // connect failed
+            transport->info.cb_tbl->on_error(transport, err, transport->info.cb_arg);
+
+        } else {
+            // update state
+            transport->connected = true;
+
+            // connect succesfull
+            transport->info.cb_tbl->on_connect(transport, transport->info.cb_arg);
+        }
+
+    } else {
+        // wrapper method
+        type->methods._connected(transport, err);
+
+    }
+}
+
+void transport_invoke (transport_t *transport, short what)
+{
+    // on_ready
+    if (what & TRANSPORT_READ && transport->info.cb_tbl->on_read)
+        transport->info.cb_tbl->on_read(transport, transport->info.cb_arg);
+
+    // on_write
+    if (what & TRANSPORT_WRITE && transport->info.cb_tbl->on_write)
+        transport->info.cb_tbl->on_write(transport, transport->info.cb_arg);
+
+}
+
+void transport_error (transport_t *transport, const error_t *err)
+{
+    // update state
+    transport->connected = false;
+
+    // invoke callback
+    transport->info.cb_tbl->on_error(transport, err, transport->info.cb_arg);
+}
+
+/*
+ * Public API
+ */
+int transport_read (transport_t *transport, void *buf, size_t len, error_t *err)
+{
+    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
+
+    // not readable
+    if (!type->methods.read)
+        return SET_ERROR(err, &transport_errors, ERR_TRANSPORT_READABLE);
+
+    // proxy off to method handler
+    if (type->methods.read(transport, buf, &len, err))
+        return -error_code(err);
+    
+    // return updated 'bytes-read' len
+    return len;
+}
+
+int transport_write (transport_t *transport, const void *buf, size_t len, error_t *err)
+{
+    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
+
+    // not writeable
+    if (!type->methods.write)
+        return SET_ERROR(err, &transport_errors, ERR_TRANSPORT_WRITEABLE);
+
+    // proxy off to method handler
+    if (type->methods.write(transport, buf, &len, err))
+        return -error_code(err);
+
+    // return updated 'bytes-written' len
+    return len;
+}
+
+err_t transport_events (transport_t *transport, short mask)
+{
+    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
+    error_t err;
+
+    // notify transport
+    if (type->methods.events) {
+        if (type->methods.events(transport, mask, &err))
+            goto error;
+    }
+
+    // update the event mask
+    transport->info.ev_mask = mask;
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(&err);
+}
+
+void transport_set_callbacks (transport_t *transport, const struct transport_callbacks *cb_tbl, void *cb_arg)
+{
+    transport->info.cb_tbl = cb_tbl;
+    transport->info.cb_arg = cb_arg;
+}
+
+void transport_destroy (transport_t *transport)
+{
+    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
+
+    // destroy the transport-specific stuff
+    if (type->methods.deinit)
+        type->methods.deinit(transport);
+
+    // then the transport itself
+    free(transport);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,179 @@
+#ifndef LIBQMSK_TRANSPORT_H
+#define LIBQMSK_TRANSPORT_H
+
+/**
+ * @file
+ *
+ * Defines a intermediate-level (as opposed to high-level or low-level) API for connected streams of data, presumeably
+ * non-blocking ones.
+ */
+#include "error.h"
+#include <stddef.h>
+
+/**
+ * Opaque transport state handle.
+ *
+ * Transports are reliable byte streams, connected with some endpoint over some medium. Common implementations are
+ * e.g. TCP, SSL or fifo transports (using the OS file/socket API).
+ *
+ * Transports can be connected or unconnected. For synchronous opens (e.g. fifo_open_read()), the transport returned
+ * will already be connected, meaning that the transport_callbacks::on_connect callback is unused. For async connects
+ * such as sock_tcp_connect()/sock_ssl_connect(), the transport returned is *not* connected, and you must wait for
+ * transport_callbacks::on_connect to be called before being able to send/recieve data on the transport.
+ *
+ * Once you have an opened transport, sending and receiving data is simple - just call transport_read()/transport_write().
+ * These implement unbuffered I/O, so they may do partial reads/writes. In terms of the system read/write calls, the
+ * main difference is in the error return codes. On EOF, instead of returning zero, they return ERR_TRANSPORT_EOF (or
+ * ERR_WRITE_EOF for transport_write, for whoever knows what that means...). This means that when the underlying
+ * transport is unable to fufill the request due to lack of data/buffer space, these can return zero to signifiy
+ * something simliar to EAGAIN.
+ *
+ * The transport API also implements non-blocking/event-based operation (usually on top of libevent), although at a
+ * slightly different level than the normal select/poll API. Instead of the user asking the transport to notify for
+ * read/write after transport_read/transport_write return zero, the transport will take care of this itself. 
+ * 
+ * Specifically, the user can supply a mask of events they are currently interested in. By default, this should be the
+ * full TRANSPORT_READ | TRANSPORT_WRITE, as the transport will take care of managing events by itself. If you wish to
+ * e.g. throttle read/write, you may set a different event mask using transport_events(), which will prevent the
+ * relevant callback from being triggered.
+ *
+ * For reads, the transport maintains a persistent read event, and will always call on_read when data is available on
+ * the socket (i.e. normal select() semantics). If masked out using transport_events(), there should be no event
+ * activity on the transport (i.e. the fd read event is removed).
+ *
+ * For writes, the transport maintains a write event that is disabled by default. If transport_write() returns zero, it will
+ * become enabled *once*, and consequently trigger transport_callbacks::on_write *once*, after which you must call
+ * transport_write() to possibly enable it again. If masked out using transport_events(), transport_write() will not
+ * enable the write event, and any pending write event is cancelled. If masked back in using transport_events(), the
+ * write event will *not* be registered, so if you have pending data, do a transport_write() after enabling
+ * TRANSPORT_WRITE.
+ *
+ * Note that transport_write() returning fewer bytes than given will *not* enable the write event! You must call
+ * transport_write() until you have either written all of your data, or it returns zero!
+ */
+struct transport;
+
+/**
+ * @see transport
+ */
+typedef struct transport transport_t;
+
+/**
+ * Errors
+ */
+enum transport_error_code {
+    ERR_TRANSPORT_NONE,
+    ERR_TRANSPORT_EOF,          ///< EOF
+    ERR_TRANSPORT_READABLE,     ///< transport not readable
+    ERR_TRANSPORT_WRITEABLE,    ///< transport not writeable
+};
+
+const struct error_list transport_errors;
+
+/**
+ * User callbacks for transports
+ *
+ * @see transport
+ */
+struct transport_callbacks {
+    /**
+     * The transport is now connected
+     */
+    void (*on_connect) (transport_t *transport, void *arg);
+
+    /**
+     * Data is now available for reading from the transport
+     */
+    void (*on_read) (transport_t *transport, void *arg);
+
+    /**
+     * The transport has become writeable
+     */
+    void (*on_write) (transport_t *transport, void *arg);
+
+    /**
+     * An asynchronous error has occured. This is only called for errors that occur while being called directly from
+     * the underlying event loop, and never from inside an API function.
+     *
+     * You must call transport_destroy to release the transport.
+     */
+    void (*on_error) (transport_t *transport, const error_t *err, void *arg);
+};
+
+/**
+ * Bitmask of available events
+ *
+ * @see transport
+ */
+enum transport_event {
+    TRANSPORT_READ  = 0x01,
+    TRANSPORT_WRITE = 0x02,
+};
+
+/**
+ * User info required to build a transport
+ *
+ * @see transport
+ */
+struct transport_info {
+    /** The callbacks table */
+    const struct transport_callbacks *cb_tbl;
+
+    /** The callback context argument */
+    void *cb_arg;
+
+    /** Initial event mask using transport_event flags */
+    short ev_mask;
+};
+
+/**
+ * Read a series of bytes from the transport into the given \a buf (up to \a len bytes). If succesfull, this returns
+ * the number of bytes read (which will be less than or equal to \a len). If the transport is nonblocking, and there is
+ * no data available, this returns zero, and need not be called again until transport_callbacks::on_read is invoked.
+ *
+ * On errors, this returns the negative error code, and more info via \a err. Note that as opposed to read(2), EOF is
+ * handled as an error, returning ERR_EOF.
+ *
+ * @param transport the transport state
+ * @param buf the buffer to read the bytes into
+ * @param len the number of bytes to read into the buffer
+ * @param err returned error info
+ * @return bytes read, zero if none available, -err_t
+ */
+int transport_read (transport_t *transport, void *buf, size_t len, error_t *err);
+
+/**
+ * Write a series of bytes from the given \a buf (containing \a len bytes) to the transport. If succesfull, this
+ * returns the number of bytes written (which may be less than \a len). If the transport is nonblocking, and the
+ * operation would have blocked, no data will be written, and zero is returned; in this case, the transport's write
+ * event is enabled (unless TRANSPORT_WRITE is masked out).
+ *
+ * On errors, this returns the negative error code, along with extended info via \a err.
+ *
+ * @param transport the transport state
+ * @param buf the buffer to write the bytes from
+ * @param len number of bytes to write
+ * @param err returned error info
+ * @return bytes written, zero if would have blocked, -err_t
+ */
+int transport_write (transport_t *transport, const void *buf, size_t len, error_t *err);
+
+/**
+ * Change the mask of enabled events.
+ */
+err_t transport_events (transport_t *transport, short mask);
+
+/**
+ * Install a new set of callback handlers, replacing the old ones.
+ */
+void transport_set_callbacks (transport_t *transport, const struct transport_callbacks *cb_tbl, void *cb_arg);
+
+/**
+ * Close and destroy the transport immediately, severing any established connection rudely.
+ *
+ * This will release all resources associated with the transport, including the transport itself, which must not be
+ * used anymore.
+ */
+void transport_destroy (transport_t *transport);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_fd.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,368 @@
+#include "transport_fd.h"
+
+#include "log.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+/**
+ * Our libevent callback
+ */
+static void transport_fd_on_event (evutil_socket_t _fd, short ev_what, void *arg) 
+{
+    struct transport_fd *fd = arg;
+
+    (void) _fd;
+
+    short what = 0;
+
+    // build flags
+    if (ev_what & EV_READ)
+        what |= TRANSPORT_READ;
+
+    if (ev_what & EV_WRITE)
+        what |= TRANSPORT_WRITE;
+
+    // invoke user callback
+    fd->cb_func(fd, what, fd->cb_arg);
+}
+
+/**
+ * Our transport_methods implementations
+ */
+err_t transport_fd__read (transport_t *transport, void *buf, size_t *len, error_t *err)
+{
+    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
+    int ret;
+
+    error_reset(err);
+    
+    // read(), and detect non-EAGAIN or EOF
+    if ((ret = read(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
+        // unexpected error
+        return SET_ERROR_ERRNO(err, &libc_errors, ERR_READ);
+    
+    else if (ret == 0)
+        // EOF
+        return SET_ERROR(err, &transport_errors, ERR_TRANSPORT_EOF);
+
+
+    if (ret < 0) {
+        // EAGAIN -> zero bytes
+        *len = 0;
+
+    } else {
+        // normal -> bytes read
+        *len = ret;
+    }
+
+    // ok
+    return SUCCESS;
+}
+
+err_t transport_fd__write (transport_t *transport, const void *buf, size_t *len, error_t *err)
+{
+    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
+    int ret;
+    
+    error_reset(err);
+    
+    // write(), and detect non-EAGAIN or EOF
+    if ((ret = write(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
+        // unexpected error
+        return SET_ERROR_ERRNO(err, &libc_errors, ERR_WRITE);
+    
+    else if (ret == 0)
+        // EOF
+        return SET_ERROR(err, &libc_errors, ERR_WRITE_EOF);
+
+
+    if (ret < 0) {
+        // EAGAIN -> zero bytes
+        *len = 0;
+
+        if (transport->info.ev_mask & TRANSPORT_WRITE)
+            // enable the write event
+            if ((ERROR_CODE(err) = transport_fd_enable(fd, TRANSPORT_WRITE)))
+                return ERROR_CODE(err);
+
+    } else {
+        // normal -> bytes read
+        *len = ret;
+    }
+
+    return SUCCESS;
+}
+
+err_t transport_fd__events (transport_t *transport, short ev_mask, error_t *err)
+{
+    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
+    
+    short mask = 0;
+
+    // enable read as requested
+    if (ev_mask & TRANSPORT_READ)
+        mask |= TRANSPORT_READ;
+    
+    // enable write if requested and it's currently enabled
+    if ((ev_mask & TRANSPORT_WRITE) && event_pending(fd->ev_write, EV_WRITE, NULL))
+        mask |= TRANSPORT_WRITE;
+
+    // set
+    return (ERROR_CODE(err) = transport_fd_events(fd, mask));
+}
+
+void transport_fd__deinit (transport_t *transport)
+{
+    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
+
+    transport_fd_deinit(fd);
+}
+
+const struct transport_type transport_fd_type = {
+    .base_type  = {
+        .parent     = &transport_type_type,
+    },
+    .methods    = {
+        .read       = transport_fd__read,
+        .write      = transport_fd__write,
+        .events     = transport_fd__events,
+        .deinit     = transport_fd__deinit
+    }
+};
+
+/**
+ * Dummy callbacks
+ */
+void transport_fd_callback_user (struct transport_fd *fd, short what, void *arg)
+{
+    (void) arg;
+    
+    // proxy
+    transport_invoke(TRANSPORT_FD_BASE(fd), what);
+}
+
+/**
+ * Function implementations
+ */
+void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd)
+{
+    // sanity-check
+    assert(!fd->fd);
+    assert(!fd->ev_read && !fd->ev_write);
+    assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0);
+
+    // initialize
+    fd->ev_base = ev_base;
+    fd->fd = _fd;
+    fd->cb_func = fd->cb_arg = NULL;
+}
+
+err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock)
+{
+    assert(fd->fd != TRANSPORT_FD_INVALID);
+
+    // XXX: maintain old flags?
+
+
+    // set new flags
+    if (fcntl(fd->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
+        return ERR_FCNTL;
+
+    return SUCCESS;
+}
+
+/**
+ * Install our internal event handler.
+ *
+ * The events should not already be set up.
+ *
+ * Cleans up partial events on errors
+ */
+err_t transport_fd_install (struct transport_fd *fd)
+{
+    assert(fd->fd != TRANSPORT_FD_INVALID);
+    assert(!fd->ev_read && !fd->ev_write);
+
+    // create new events
+    if ((fd->ev_read = event_new(fd->ev_base, fd->fd, EV_READ | EV_PERSIST, transport_fd_on_event, fd)) == NULL)
+        goto err_event_add;
+
+    if ((fd->ev_write = event_new(fd->ev_base, fd->fd, EV_WRITE, transport_fd_on_event, fd)) == NULL)
+        goto err_event_add;
+    
+    // ok
+    return SUCCESS;
+
+err_event_add:
+    // remove partial events
+    transport_fd_clear(fd);
+
+    return ERR_EVENT_NEW;
+}
+
+err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg)
+{
+    // requires a valid fd
+    assert(fd->fd != TRANSPORT_FD_INVALID);
+    
+    // store
+    fd->cb_func = cb_func;
+    fd->cb_arg = cb_arg;
+    
+    // install the event handlers?
+    if (!fd->ev_read || !fd->ev_write)
+        return transport_fd_install(fd);
+    else
+        return SUCCESS;
+}
+
+err_t transport_fd_enable (struct transport_fd *fd, short mask)
+{
+    // just add the appropriate events
+    if (mask & TRANSPORT_READ && event_add(fd->ev_read, NULL))
+        return ERR_EVENT_ADD;
+ 
+    if (mask & TRANSPORT_WRITE && event_add(fd->ev_write, NULL))
+        return ERR_EVENT_ADD;
+    
+
+    return SUCCESS;
+}
+
+err_t transport_fd_disable (struct transport_fd *fd, short mask)
+{
+    if (mask & TRANSPORT_READ && event_del(fd->ev_read))
+        return ERR_EVENT_DEL;
+
+    if (mask & TRANSPORT_WRITE && event_del(fd->ev_write))
+        return ERR_EVENT_DEL;
+
+
+    return SUCCESS;
+}
+
+err_t transport_fd_events (struct transport_fd *fd, short mask)
+{
+    err_t err;
+    
+    // enable/disable read
+    if (mask & TRANSPORT_READ)
+        err = event_add(fd->ev_read, NULL);
+    else
+        err = event_del(fd->ev_read);
+
+    if (err)
+        return err;
+
+    // enable/disable write
+    if (mask & TRANSPORT_WRITE)
+        err = event_add(fd->ev_write, NULL);
+    else
+        err = event_del(fd->ev_write);
+
+    if (err)
+        return err;
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Remove our current ev_* events, but leave the cb_* intact.
+ */
+static void transport_fd_remove (struct transport_fd *fd)
+{
+    if (fd->ev_read)
+        event_free(fd->ev_read);
+
+    if (fd->ev_write)
+        event_free(fd->ev_write);
+    
+    fd->ev_read = NULL;
+    fd->ev_write = NULL;
+}
+
+void transport_fd_clear (struct transport_fd *fd)
+{
+    // remove the events
+    transport_fd_remove(fd);
+    
+    // clear the callbacks
+    fd->cb_func = fd->cb_arg = NULL;
+}
+
+err_t transport_fd_defaults (struct transport_fd *fd)
+{
+    error_t err;
+
+    // install the transport_invoke callback handler
+    if ((ERROR_CODE(&err) = transport_fd_setup(fd, transport_fd_callback_user, NULL)))
+        goto error;
+
+    // enable read unless masked out
+    if (TRANSPORT_FD_BASE(fd)->info.ev_mask & TRANSPORT_READ) {
+        if ((ERROR_CODE(&err) = transport_fd_enable(fd, TRANSPORT_READ)))
+            goto error;
+    }
+    
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(&err);
+}
+
+err_t transport_fd_set (struct transport_fd *fd, int _fd, error_t *err)
+{
+    assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0);
+
+    // close the old stuff
+    if (transport_fd_close(fd, err))
+        log_warn_error(err, "close");
+    
+    // set the new one
+    fd->fd = _fd;
+    
+    // do we have callbacks that we need to setup?
+    if (fd->cb_func)
+        return transport_fd_install(fd);
+
+    else 
+        return SUCCESS;
+}
+
+void transport_fd_invoke (struct transport_fd *fd, short what)
+{
+    // invoke
+    transport_invoke(TRANSPORT_FD_BASE(fd), what);
+}
+
+err_t transport_fd_close (struct transport_fd *fd, error_t *err)
+{
+    int _fd = fd->fd;
+
+    // remove any installed events
+    transport_fd_remove(fd);
+
+    // invalidate fd
+    fd->fd = TRANSPORT_FD_INVALID;
+ 
+    // close the fd
+    if (_fd != TRANSPORT_FD_INVALID && close(_fd))
+        return SET_ERROR_ERRNO(err, &libc_errors, ERR_CLOSE);
+
+    return SUCCESS;
+}
+
+void transport_fd_deinit (struct transport_fd *fd)
+{
+    error_t err;
+
+    // XXX: this might block
+    if (transport_fd_close(fd, &err))
+        log_warn_error(&err, "close");
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_fd.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,173 @@
+#ifndef LIBQMSK_TRANSPORT_FD_H
+#define LIBQMSK_TRANSPORT_FD_H
+
+/**
+ * @file
+ *
+ * Support for transport implementations that use POSIX file descriptor streams.
+ *
+ * This provides the read/write methods, as well as functions to implement the event-based behaviour.
+ */
+#include "transport_internal.h"
+
+#include <event2/event.h>
+#include <stdbool.h>
+
+// forward-declare
+struct transport_fd;
+
+/**
+ * Our transport_type
+ */
+extern const struct transport_type transport_fd_type;
+
+/**
+ * Low-level callback
+ */
+typedef void (*transport_fd_callback_func) (struct transport_fd *fd, short what, void *arg);
+
+/**
+ * The fd-based transport implementation
+ */
+struct transport_fd {
+    /** Base transport state */
+    struct transport base;
+
+    /** Libevent base to use */
+    struct event_base *ev_base;
+    
+    /** OS file descriptor */
+    evutil_socket_t fd;
+
+    /** IO events */
+    struct event *ev_read, *ev_write;
+
+    /** Low-level callback */
+    transport_fd_callback_func cb_func;
+
+    /** Callback context argument */
+    void *cb_arg;
+
+};
+
+/**
+ * Get a transport_t pointer from a transport_fd
+ */
+#define TRANSPORT_FD_BASE(tp_ptr) (&(tp_ptr)->base)
+
+/**
+ * Invalid OS FD
+ */
+#define TRANSPORT_FD_INVALID ((evutil_socket_t) -1)
+
+/**
+ * Implementation of transport_methods::read
+ */
+err_t transport_fd__read (transport_t *transport, void *buf, size_t *len, error_t *err);
+
+/**
+ * Implementation of transport_methods::write.
+ *
+ * If this gets EAGAIN, it will automatically enable the write event, unless masked out.
+ */
+err_t transport_fd__write (transport_t *transport, const void *buf, size_t *len, error_t *err);
+
+/**
+ * Implementation of transport_methods::events.
+ *
+ * For TRANSPORT_READ, this will simply apply enable/disable as given.
+ *
+ * For TRANSPORT_WRITE, the write event will only be enabled if given in the mask, *and* the ev_write event is currently
+ * active (via transport_fd_methods_write()); otherwise, the write event will not be enabled.
+ */
+err_t transport_fd__events (transport_t *transport, short mask, error_t *err);
+
+/**
+ * Implementation of transport_methods::deinit.
+ *
+ * This simply calls transport_fd_deinit().
+ */
+void transport_fd__deinit (transport_t *transport);
+
+/**
+ * A transport_fd_callback_func that simply invokes the transport_callback user functions.
+ *
+ * Register with a NULL cb_arg.
+ */
+void transport_fd_callback_user (struct transport_fd *fd, short what, void *arg);
+
+
+
+
+/**
+ * Initialize the transport_fd to use the given, connected fd, or TRANSPORT_FD_INVALID if we don't yet have an fd.
+ *
+ * It is an error to call this if the transport_fd already has an fd set
+ *
+ * @param fd the transport_fd state
+ * @param ev_base the libevent base to use
+ * @param _fd the OS file descriptor, or TRANSPORT_FD_INVALID
+ */
+void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd);
+
+/**
+ * Set the fd's nonblocking mode using fcntl.
+ */
+err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock);
+
+/**
+ * Set or replace the fd's event callback. The callback will not be enabled.
+ *
+ * The transport must have a valid fd bound to it.
+ */
+err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg);
+
+/**
+ * Enable the specified events, any of { TRANSPORT_READ, TRANSPORT_WRITE }.
+ */
+err_t transport_fd_enable (struct transport_fd *fd, short mask);
+
+/**
+ * Disable the specified events, any of { TRANSPORT_READ, TRANSPORT_WRITE }.
+ */
+err_t transport_fd_disable (struct transport_fd *fd, short mask);
+
+/**
+ * Set the enable/disable state of our events to the given mask.
+ */
+err_t transport_fd_events (struct transport_fd *fd, short mask);
+
+/**
+ * Remove any old event callback present, so it will not be called anymore.
+ *
+ * It is perfectly safe to call this without any callbacks installed.
+ */
+void transport_fd_clear (struct transport_fd *fd);
+
+/**
+ * Setup and enable the default event handlers for transport operation, that is, use transport_fd_callback_user as the 
+ * callback and enable TRANSPORT_READ, unless masked out.
+ */
+err_t transport_fd_defaults (struct transport_fd *fd);
+
+/**
+ * Replace the old fd with a new one, maintaining any event callbacks set with transport_fd_callback. If any events were
+ * enabled before, they are not enabled anymore.
+ */
+err_t transport_fd_set (struct transport_fd *fd, int _fd, error_t *err);
+
+/**
+ * Close an opened fd, releasing all resources within our state.
+ */
+err_t transport_fd_close (struct transport_fd *fd, error_t *err);
+
+/**
+ * Deinitialize the transport_fd.
+ *
+ * This logs a warning if the close() fails.
+ *
+ * XXX: this may actually block, I think? SO_LINGER?
+ */
+void transport_fd_deinit (struct transport_fd *fd);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_internal.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,126 @@
+#ifndef LIBQMSK_TRANSPORT_INTERNAL_H
+#define LIBQMSK_TRANSPORT_INTERNAL_H
+
+/**
+ * @file
+ *
+ * The internal interface for transport implementations.
+ */ 
+#include "transport.h"
+#include "object.h"
+
+#include <stdbool.h>
+
+/**
+ * The object_type for a transport_type
+ */
+extern const struct object_type transport_type_type;
+
+/**
+ * The definition of a transport type
+ */
+struct transport_type {
+    struct object_type base_type;
+
+    /**
+     * Method table for implementation stuff.
+     *
+     * Note that it is the transport's resposibility to implement the behaviour described in transport.h
+     */
+    struct transport_methods {
+        /** For transport_read() */
+        err_t (*read) (transport_t *transport, void *buf, size_t *len, error_t *err);
+
+        /** For transport_write() */
+        err_t (*write) (transport_t *transport, const void *buf, size_t *len, error_t *err);
+
+        /**
+         * The mask of event flags will be set to the given mask if this method is succesfull.
+         *
+         * The old mask is still available in transport::info::ev_mask.
+         */
+        err_t (*events) (transport_t *transport, short mask, error_t *err);
+
+        /** 
+         * Release the transport's internal state, but not the transport itself.
+         *
+         * In other words, this should release everything inside the transport_t, but not free() the transport_t itself.
+         */
+        void (*deinit) (transport_t *transport);
+
+        /**
+         * Used by layered transports to handle transport_connected.
+         *
+         * If this is NULL, transport_connected will call the user callback directly, otherwise, it will proxy through this.
+         *
+         * The \a err param follows the same rules as for transport_connected() - NULL for success, error info otherwise.
+         *
+         * @param transport the transport state
+         * @param err error info if the connect failed
+         */
+        void (*_connected) (transport_t *transport, const error_t *err);
+    } methods;
+};
+
+/**
+ * The base transport type
+ */
+struct transport {
+    struct object base_obj;
+
+    /** User info */
+    struct transport_info info;
+    
+    /** Are we connected? */
+    bool connected;
+};
+
+/**
+ * Bind the given transport to the given type with the given user info.
+ *
+ * \a info may be given as NULL to not have any callbacks, but this will crash if any transport_* is called before
+ * transport_set_callbacks().
+ *
+ * It is a bug to call this with a transport that is already bound.
+ */
+void transport_init (transport_t *transport, const struct transport_type *type, const struct transport_info *info);
+
+/**
+ * Check the type of the transport, and return the transport as a void* suitable for casting to the appropriate struct
+ * for the type, or any of its children.
+ *
+ * It is a bug to call this with a transport of a different type.
+ */
+void* transport_check (transport_t *transport, const struct transport_type *type);
+
+/**
+ * Mark the transport as connected, calling transport_methods::_connected if it exists and \a direct is not given,
+ * transport_callbacks::on_connected/transport_callbacks::on_error otherwise.
+ *
+ * If the connect succeeded, \a err should be given as NULL. If the connect failed, \a err should contain the error
+ * info.
+ *
+ * If called from the transport_methods::_connected method, pass in direct to avoid recursion.
+ *
+ * This sets the transport::connected flag before calling transport_callbacks::on_connected (i.e. directly) without any
+ * error set.
+ *
+ * XXX: implement proper layering of types by taking a transport_type arg and chaining down from there.
+ *
+ * @param transport the transport state
+ * @param err NULL for success, otherwise connect error code
+ * @param direct call the user callback directly, ignoring any method
+ */
+void transport_connected (transport_t *transport, const error_t *err, bool direct);
+
+/**
+ * Invoke the user callbacks based on the given TRANSPORT_* flags
+ */
+void transport_invoke (transport_t *transport, short what);
+
+/**
+ * Mark the transport as failed, calling transport_methods::on_error with the given error code.
+ */
+void transport_error (transport_t *transport, const error_t *err);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_test.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,366 @@
+#include "transport_test.h"
+#include "transport_internal.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+
+/**
+ * Simple IO vector
+ */
+struct io_vec {
+    /** The buffer */
+    char *buf;
+
+    /** Buffer size */
+    size_t len;
+};
+
+/**
+ * Simple vectored IO-buffer
+ */
+struct io_buf {
+    /** The array of buffer-vectors, {NULL}-terminated */
+    struct io_vec *vecs;
+
+    /** The number of io_vecs */
+    size_t count;
+
+    /** Current read/write vector */
+    struct io_vec *read_vec, *write_vec;
+
+    /** Offset into current vector */
+    size_t off;
+};
+
+/**
+ * Forward-declare our transport_type
+ */
+extern const struct transport_type transport_test_type;
+
+
+/**
+ * A dummy sock_stream implementation intended for testing purposes.
+ */
+struct transport_test {
+    /** The base transport stuff */
+    struct transport base;
+
+    /** The send/recieve buffers */
+    struct io_buf send_buf, recv_buf;
+
+    /** No more data is going to be added, return EOF once all the rest is consumed */
+    bool eof;
+};
+
+/**
+ * Get a transport pointer from a transport_test pointer
+ */
+#define TRANSPORT_TEST_BASE(tp_ptr) (&(tp_ptr)->base)
+
+/**
+ * Grow buf->vecs if needed to ensure that buf->write_vec points to a valid io_vec
+ */
+static err_t io_buf_grow (struct io_buf *buf)
+{
+    size_t read_vec_offset = buf->read_vec ? (buf->read_vec - buf->vecs) : 0;
+    size_t write_vec_offset = buf->write_vec ? (buf->write_vec - buf->vecs) : 0;
+    struct io_vec *v;
+    struct io_vec *vecs_tmp = buf->vecs;
+
+    // don't grow if not full
+    if (buf->vecs && buf->write_vec < buf->vecs + buf->count)
+        return SUCCESS;
+
+    // new size
+    buf->count = buf->count * 2 + 1;
+
+    // grow
+    if ((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec))) == NULL) {
+        // restore old value
+        buf->vecs = vecs_tmp;
+
+        return ERR_CALLOC;
+    }
+
+    // restore vec positions
+    buf->write_vec = buf->vecs + write_vec_offset;
+    buf->read_vec = buf->vecs + read_vec_offset;
+
+    // zero new vecs
+    for (v = buf->write_vec; v < buf->vecs + buf->count; v++)
+        memset(v, 0, sizeof(*v));
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Write some data to an io_buf, copying it.
+ */
+static err_t io_buf_write (struct io_buf *buf, const char *data, size_t len)
+{
+    error_t err;
+
+    // ensure there's room
+    if ((ERROR_CODE(&err) = io_buf_grow(buf)))
+        goto error;
+    
+    // the vector to use
+    struct io_vec *vec = buf->write_vec;
+
+    // allocate
+    if ((vec->buf = malloc(len)) == NULL)
+        JUMP_SET_ERROR(&err, ERR_MEM);
+    
+    // store
+    vec->len = len;
+    memcpy(vec->buf, data, len);
+
+    // vec consumed
+    buf->write_vec++;
+
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(&err);
+}
+
+/**
+ * Destroy the io_buf, freeing all resources.
+ *
+ * The io_buf must not be used anymore.
+ */
+static void io_buf_destroy (struct io_buf *buf)
+{
+    size_t i;
+
+    // free the io_vec buffers
+    for (i = 0; i < buf->count; i++) {
+        free(buf->vecs[i].buf);
+    }
+    
+    // free the vector list
+    free(buf->vecs);
+}
+
+/**
+ * transport_methods::read implementation.
+ */
+static err_t transport_test__read (transport_t *transport, void *buf_ptr, size_t *len, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+    struct io_buf *buf = &tp->recv_buf;
+    struct io_vec *vec = buf->read_vec;
+    
+    // EOF/nonblock if we're past the end of the last vector
+    if (!vec || vec == buf->vecs + buf->count || buf->off >= vec->len) {
+        if (!tp->eof) {
+            // wait for more to be fed in
+            *len = 0;
+            return SUCCESS;
+
+        } else {
+            // EOF!
+            return SET_ERROR(err, ERR_EOF);
+        }
+    }
+    
+    // amount of data available in this iovec
+    size_t available = vec->len - buf->off;
+
+    // amount to read
+    size_t to_read = *len;
+    
+    // trim down?
+    if (to_read > available)
+        to_read = available;
+
+    // copy
+    memcpy(buf_ptr, vec->buf + buf->off, to_read);
+    
+
+    if (to_read < available) {
+        // bytes still left in the vector
+        buf->off += to_read;
+
+    } else {
+        // consumed the whole vector
+        // XXX: release data?
+        buf->read_vec++;
+        buf->off = 0;
+    }
+
+    // update len
+    *len = to_read;
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * transport_methods::write implementation.
+ */
+static err_t transport_test__write (transport_t *transport, const void *data, size_t *len, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    // write it out
+    // XXX: partial writes?
+    if ((ERROR_CODE(err) = io_buf_write(&tp->send_buf, data, *len)))
+        goto error;
+    
+    // ok
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);
+}
+
+static err_t transport_test__events (transport_t *transport, short mask, error_t *err)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    (void) tp;
+    (void) mask;
+    (void) err;
+    
+    // XXX: don't re-trigger anything
+    
+    return SUCCESS;
+}
+
+static void transport_test__deinit (transport_t *transport)
+{
+    struct transport_test *tp = transport_check(transport, &transport_test_type);
+
+    transport_test_destroy(tp);
+}
+
+/*
+ * Our sock_stream_type
+ */
+const struct transport_type transport_test_type = {
+    .base_type = {
+        .parent     = &transport_type_type,
+    },
+    .methods                = {
+        .read       = transport_test__read,
+        .write      = transport_test__write,
+        .events     = transport_test__events,
+        .deinit     = transport_test__deinit
+    },
+};
+
+struct transport_test* transport_test_create (struct transport_info *info)
+{
+    struct transport_test *tp;
+
+    // allocate
+    assert((tp = calloc(1, sizeof(*tp))));
+    
+    // initialize base with our transport_type
+    transport_init(TRANSPORT_TEST_BASE(tp), &transport_test_type, info);
+
+    // ok
+    return tp;
+}
+
+transport_t* transport_test_cast (struct transport_test *tp)
+{
+    return TRANSPORT_TEST_BASE(tp);
+}
+
+void transport_test_event (struct transport_test *tp, short what)
+{
+    // invoke, masking out as needed
+    // this won't do anything if all the bits are masked out
+    transport_invoke(TRANSPORT_TEST_BASE(tp), what & TRANSPORT_TEST_BASE(tp)->info.ev_mask);
+}
+
+void transport_test_push_buf (struct transport_test *tp, const char *data, size_t len)
+{
+    // push it
+    assert(io_buf_write(&tp->recv_buf, data, len) == SUCCESS);
+    
+    // notify
+    transport_test_event(tp, TRANSPORT_READ);
+}
+
+void transport_test_push_str (struct transport_test *tp, const char *str)
+{
+    // push it
+    transport_test_push_buf(tp, str, strlen(str));
+}
+
+void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...)
+{
+    char buf[TRANSPORT_TEST_FMT_MAX];
+    size_t ret;
+
+    // format
+    va_list vargs; va_start(vargs, fmt);
+    assert((ret = vsnprintf(buf, sizeof(buf), fmt, vargs)) <= sizeof(buf));
+    va_end(vargs);
+       
+    // push it
+    transport_test_push_buf(tp, buf, ret);
+}
+
+void transport_test_push_eof (struct transport_test *tp)
+{
+    // update state
+    tp->eof = true;
+
+    transport_test_event(tp, TRANSPORT_READ);
+}
+
+void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr)
+{
+    struct io_buf *buf = &tp->send_buf;
+    size_t len = 0, i, off = 0;
+    char *out;
+    
+    // calculate total size
+    for (i = 0; i < buf->count; i++) {
+        len += buf->vecs[i].len;
+    }
+
+    // alloc
+    assert((out = malloc(len)));
+
+    // copy
+    for (i = 0; i < buf->count; i++) {
+        struct io_vec *vec = buf->vecs + i;
+
+        memcpy(out + off, vec->buf, vec->len);
+        off += vec->len;
+        
+        // zero
+        free(vec->buf); vec->buf = NULL;
+        vec->len = 0;
+    }
+    
+    // update return
+    *buf_ptr = out;
+    *len_ptr = len;
+
+    // update write_vec
+    buf->write_vec = buf->vecs;
+}
+
+void transport_test_async_error (struct transport_test *tp, const error_t *err)
+{
+    transport_error(&tp->base, err);
+}
+
+void transport_test_destroy (struct transport_test *tp)
+{
+    // free the buffers
+    io_buf_destroy(&tp->send_buf);
+    io_buf_destroy(&tp->recv_buf);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/transport_test.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,79 @@
+#ifndef LIBQMSK_TRANSPORT_TEST_H
+#define LIBQMSK_TRANSPORT_TEST_H
+
+/**
+ * @file
+ *
+ * Dummy transport implemention for local testing.
+ */
+#include "transport.h"
+
+/**
+ * The opaque transport state
+ */
+struct transport_test;
+
+/**
+ * Construct a new, empty, connected transport_test.
+ */
+struct transport_test* transport_test_create (struct transport_info *info);
+
+/**
+ * A transport_test is a valid transport, this performs the cast
+ */
+transport_t* transport_test_cast (struct transport_test *tp);
+
+/**
+ * Invoke the transport's user callbacks for the given event mask, unless masked out.
+ */
+void transport_test_event (struct transport_test *tp, short what);
+
+/**
+ * Adds a data buffer to the recieve buffer.
+ *
+ * The given data is copied.
+ *
+ * If events are enabled, they are triggered.
+ */
+void transport_test_push_buf (struct transport_test *tp, const char *buf, size_t len);
+
+/**
+ * Add a string to the recieve buffer using transport_test_push_buf()
+ */
+void transport_test_push_str (struct transport_test *tp, const char *str);
+
+/**
+ * Maximum length of a formatted string pushed
+ */
+#define TRANSPORT_TEST_FMT_MAX 4096
+
+/**
+ * Add a formatted string to the recieve buffer
+ *
+ * @see TRANSPORT_TEST_FMT_MAX
+ */
+void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...);
+
+/**
+ * Set EOF on recv.
+ */
+void transport_test_push_eof (struct transport_test *tp);
+
+/**
+ * Get the send buffer contents as a single buffer, free() after use.
+ *
+ * This clears the send buffer, so this doesn't return the same data twice.
+ */
+void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr);
+
+/**
+ * Send async error
+ */
+void transport_test_async_error (struct transport_test *tp, const error_t *err);
+
+/**
+ * Destroy the transport buffer, releasing any buffers we allocated ourself
+ */
+void transport_test_destroy (struct transport_test *tp);
+
+#endif /* TRANSPORT_TEST_H */
--- a/src/line_proto.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,336 +0,0 @@
-
-#include "line_proto.h"
-#include "log.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-/*
- * Our state
- */
-struct line_proto {
-    /* The transport we read/write with */
-    transport_t *transport;
-
-    /* The incoming/outgoing line buffer */
-    char *in_buf, *out_buf;
-
-    /* Buffer size (same for both) */
-    size_t buf_len;
-
-    /* Offset of trailing data in buf */
-    size_t tail_offset;
-
-    /* Length of trailing data in buf, if any */
-    size_t tail_len;
-
-    /* Amount of data in the out buffer */
-    size_t out_offset;
-
-    /* Last error */
-    struct error_info err;
-
-    /* Callback info */
-    struct line_proto_callbacks callbacks;
-    void *cb_arg;
-};
-
-/**
- * An error occured which we could not recover from; the line_proto should now be considered corrupt.
- *
- * Notify the user callback, which will probably call line_proto_release().
- */
-static void line_proto_set_error (struct line_proto *lp)
-{
-    // copy error_info, as it might get free'd
-    struct error_info err = lp->err;
-
-    // trigger callback
-    lp->callbacks.on_error(&err, lp->cb_arg);
-}
-
-/**
- * Our transport_callbacks::on_read handler
- */
-static void line_proto_on_read (transport_t *transport, void *arg)
-{
-    struct line_proto *lp = arg;
-    char *line;
-
-    (void) transport;
-
-    // sanity-check
-    assert(lp->tail_offset < lp->buf_len);
-    
-    do {
-        // attempt to read a line
-        if (line_proto_recv(lp, &line))
-            // faaail
-            return line_proto_set_error(lp);
-
-        // got a line?
-        if (line)
-            lp->callbacks.on_line(line, lp->cb_arg);
-
-    } while (line);
-}
-
-/*
- * Signal for write
- */
-static void line_proto_on_write (transport_t *transport, void *arg)
-{
-    struct line_proto *lp = arg;
-    int ret;
-
-    (void) transport;
-
-    // just flush
-    if ((ret = line_proto_flush(lp)) < 0)
-        // faaail
-        return line_proto_set_error(lp);
-}
-
-// XXX: implement on_error!
-static const struct transport_callbacks line_proto_transport_callbacks = {
-    .on_read    = &line_proto_on_read,
-    .on_write   = &line_proto_on_write,
-};
-
-err_t line_proto_create (struct line_proto **lp_ptr, transport_t *transport, size_t buf_size,
-        const struct line_proto_callbacks *callbacks, void *cb_arg, error_t *err)
-{
-    struct line_proto *lp;
-
-    // alloc
-    if ((lp = calloc(1, sizeof(*lp))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // store
-    lp->transport = transport;
-    lp->buf_len = buf_size;
-    lp->callbacks = *callbacks;
-    lp->cb_arg = cb_arg;
-
-    // allocate buffers
-    if (
-            (lp->in_buf = malloc(buf_size)) == NULL
-        ||  (lp->out_buf = malloc(buf_size)) == NULL
-    )
-        JUMP_SET_ERROR(err, ERR_CALLOC);
-
-    // setup the transport
-    transport_set_callbacks(transport, &line_proto_transport_callbacks, lp);
-    
-    if ((ERROR_CODE(err) = transport_events(transport, TRANSPORT_READ | TRANSPORT_WRITE)))
-        goto error;
-
-    // return
-    *lp_ptr = lp;
-
-    return SUCCESS;
-
-error:
-    // cleanup the lp
-    line_proto_destroy(lp);
-
-    return ERROR_CODE(err);
-}
-
-/*
- * This looks for a full '\r\n' terminated line at the beginning of the given buffer. If found, the \r\n will be
- * replaced with a '\0', and the offset to the beginning of the next line returned. If not found, zero is returned
- * (which is never a valid next-line offset).
- *
- * The given \a hint is an hint as to the offset at which to start scanning, used for incremental invocations of this
- * on the same buffer.
- *
- */
-int _parse_line (char *buf, size_t len, size_t *hint) {
-    size_t i, next = 0;
-
-    // empty buffer -> nothing
-    if (len == 0)
-        return 0;
-
-    // look for terminating '\r\n' or '\n' sequence
-    for (i = *hint; i < len; i++) {
-        // match this + next char?
-        if (i < len - 1 && buf[i] == '\r' && buf[i + 1] == '\n') {
-            next = i + 2;
-            break;
-
-        } else if (buf[i] == '\n') {
-            next = i + 1;
-            break;
-        }
-    }
-
-    // searched the whole buffer?
-    if (i >= len) {
-        // do continue one char back, to keep any \r
-        *hint = len - 1;
-        return 0;
-    }
-
-    // mangle the newline off
-    buf[i] = '\0';
-
-    // return offset to next line, as set in loop based on delim
-    return next;
-}
-
-err_t line_proto_recv (struct line_proto *lp, char **line_ptr)
-{
-    // offset to recv() new data into, offset to _parse_line hint, offset to next line from _parse_line
-    size_t recv_offset = 0, peek_offset = 0, next_offset = 0;
-    int ret;
-
-    // adjust offset to beyond previous data (as will be moved next)
-    recv_offset = lp->tail_len;
-
-    // move trailing data from previous line to front of buffer
-    if (lp->tail_offset) {
-        // move to front, no-op if tail_len is zero
-        memmove(lp->in_buf, lp->in_buf + lp->tail_offset, lp->tail_len);
-
-        // reset
-        lp->tail_offset = 0;
-        lp->tail_len = 0;
-    }
-
-    // readline loop
-    do {
-        // parse any line at the beginning of the buffer
-        if ((next_offset = _parse_line(lp->in_buf, recv_offset, &peek_offset)) > 0) {
-            // store a valid *line_ptr
-            *line_ptr = lp->in_buf;
-            
-            // exit loop and return
-            break;
-        }
-
-        // ensure there's enough space for the rest of the line
-        if (recv_offset >= lp->buf_len)
-            return ERR_LINE_TOO_LONG;
-        
-        // otherwise, read more data
-        if ((ret = transport_read(lp->transport, lp->in_buf + recv_offset, lp->buf_len - recv_offset, &lp->err)) < 0)
-            return ERROR_CODE(&lp->err);
-
-        // EAGAIN?
-        if (ret == 0) {
-            // return a NULL *line_ptr
-            *line_ptr = NULL;
-            break;
-        }
-        
-        // update recv_offset
-        recv_offset += ret;
-
-    } while (1);
-
-    // update state for next call
-    lp->tail_offset = next_offset;
-    lp->tail_len = recv_offset - next_offset;
-
-    // ok
-    return SUCCESS;
-}
-
-int line_proto_send (struct line_proto *lp, const char *line)
-{
-    int ret;
-    size_t len = strlen(line), ret_len;
-
-    // drop line if we already have output buffered
-    if (lp->out_offset)
-        return -ERR_LINE_TOO_LONG;
-    
-    // try and write the line
-    if ((ret = transport_write(lp->transport, line, len, &lp->err)) < 0)
-        return -ERROR_CODE(&lp->err);
-
-    // length of the sent data
-    ret_len = ret;
-
-    // EAGAIN or partial?
-    if (ret_len < len) {
-        size_t trailing = len - ret_len;
-
-        // ensure it's not waaaay too long
-        if (trailing > lp->buf_len)
-            return -ERR_LINE_TOO_LONG;
-
-        // copy remaining portion to buffer
-        memcpy(lp->out_buf, line + ret_len, trailing);
-
-        // update offset
-        lp->out_offset = trailing;
-        
-        // buffered... transport should invoke on_write itself
-        return 1;
-
-    } else {
-        // ok, no buffering needed
-        return SUCCESS;
-
-    }
-}
-
-int line_proto_flush (struct line_proto *lp)
-{
-    int ret;
-    size_t ret_len;
-
-    assert(lp->out_offset);
-
-    // try and write the line
-    if ((ret = transport_write(lp->transport, lp->out_buf, lp->out_offset, &lp->err)) < 0)
-        return -ERROR_CODE(&lp->err);
-
-    ret_len = ret;
-
-    // empty now?
-    if (ret_len == lp->out_offset) {
-        lp->out_offset = 0;
-
-        return SUCCESS;
-    }
-
-    // partial?
-    if (ret_len > 0) {
-        size_t remaining = lp->out_offset - ret_len;
-
-        // move the rest up
-        memmove(lp->out_buf, lp->out_buf + ret_len, remaining);
-
-        // update offset
-        lp->out_offset = remaining;
-    }
-
-    // ok
-    return 1;
-}
-
-const struct error_info* line_proto_error (struct line_proto *lp)
-{
-    // return pointer
-    return &lp->err;
-}
-
-void line_proto_destroy (struct line_proto *lp)
-{
-    // free buffers
-    free(lp->in_buf);
-    free(lp->out_buf);
-
-    // socket?
-    if (lp->transport)
-        transport_destroy(lp->transport);
-
-    // free the state itself
-    free(lp);
-}
-
--- a/src/line_proto.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-#ifndef LINE_PROTO_H
-#define LINE_PROTO_H
-
-/**
- * @file
- *
- * Support for protocols that send/receive lines
- */
-#include "transport.h"
-#include "error.h"
-
-/**
- * The line_proto state handle
- */
-struct line_proto;
-
-/**
- * User callbacks for event-based line_proto behaviour
- */
-struct line_proto_callbacks {
-    /** Handle received line */
-    void (*on_line) (char *line, void *arg);
-    
-    /** Transport failed, the line_proto is corrupt, you should call line_proto_release next. */
-    void (*on_error) (const error_t *err, void *arg);
-};
-
-/**
- * Create a new line_proto off the the given sock_stream. The newly allocated line_proto will be returned via *lp_ptr.
- *
- * The incoming lines are buffered in a buffer of \a buf_size bytes. This imposes a maximum limit on the line length.
- *
- * In case of errors, \a transport will be destroyed in any case.
- *
- * @param lp_ptr a pointer to the new line_proto will be returned via this pointer
- * @param transport the connected transport to use
- * @param buf_size the incoming/outgoing buffer size, should be enough to hold the biggest possible line
- * @param callbacks the callbacks to use, a copy is stored
- * @param cb_arg the read_cb callback argument
- * @param err error information is returned via this pointer
- */
-err_t line_proto_create (struct line_proto **lp_ptr, transport_t *transport, size_t buf_size,
-        const struct line_proto_callbacks *callbacks, void *cb_arg, error_t *err);
-
-/**
- * Runs transport_read() with our internal buffer. If a full line was received, a pointer to our internal bufffer is
- * returned via *line_ptr, and we return SUCCESS. If we don't yet have a full line, and receiving more would block,
- * NULL is returned via *line_ptr instead. Otherwise, nonzero error return code.
- *
- * @param line_ptr a pointer to the received line is returned via this pointer
- */
-err_t line_proto_recv (struct line_proto *lp, char **line_ptr);
-
-/**
- * Write a single line to the sock_stream, buffering any incomplete fragment that remains unsent. Returns zero if the
- * line was succesfully sent, >0 if it was only partially sent, or -err on errors.
- *
- * The given line should already include the terminating '\r\n' character sequence.
- *
- * @param line pointer to buffer containing \r\n\0 terminated line
- */
-int line_proto_send (struct line_proto *lp, const char *line);
-
-/**
- * Flush out any buffered line fragment. Returns zero if the buffer was flushed empty, >0 if there's still fragments
- * remaining, or -err on errors.
- *
- * It is a bug to call this if there is no data waiting to be sent.
- */
-int line_proto_flush (struct line_proto *lp);
-
-/**
- * Get current error_info*
- */
-const error_t* line_proto_error (struct line_proto *lp);
-
-/**
- * Destroy any buffers and the underlying transport.
- *
- * This does not close the connection cleanly, and is intended for use to abort after errors.
- */
-void line_proto_destroy (struct line_proto *lp);
-
-#endif /* LINE_PROTO_H */
--- a/src/lua_func.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-#include "lua_func.h"
-#include "error.h"
-
-#include <lua5.1/lauxlib.h>
-
-/**
- * Pushes onto the stack the value at t[i]
- */
-static void lua_getindex (lua_State *L, int t, int i)
-{
-    lua_pushinteger(L, i);
-    lua_gettable(L, t);
-}
-
-/**
- * Pushes onto the stack either:
- *  * the value at t[name]
- *  * the value at t[index]
- *
- *  Returns the new index, or 0, if neither could be found
- */
-static int lua_arg_lookup (lua_State *L, int t, const char *name, int index)
-{
-    // try name
-    lua_getfield(L, t, name);
-
-    if (!lua_isnil(L, -1))
-        return lua_gettop(L);
-    else
-        lua_pop(L, 1);
-    
-    // try index
-    lua_getindex(L, t, index);
-    
-    if (!lua_isnil(L, -1))
-        return lua_gettop(L);
-
-    else
-        lua_pop(L, 1);
-    
-    // not found
-    return 0;
-}
-
-static const char *_lua_arg_string (lua_State *L, int index, const char *name, const char *def)
-{
-    const char *value;
-
-    // use default?
-    if (lua_isnoneornil(L, index) && def != (const char *) LUA_ARG_REQUIRED)
-        return def;
-    
-    // value given?
-    if ((value = lua_tostring(L, index)))
-        return value;
-   
-    // error
-    luaL_error(L, "missing value for required string argument <%d:%s>", index, name); return NULL;
-}
-
-static bool _lua_arg_bool (lua_State *L, int index, const char *name, int def)
-{
-    (void) name;
-
-    // use default?
-    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
-        return def;
-   
-    // value given
-    return lua_toboolean(L, index);
-}
-
-static long _lua_arg_int (lua_State *L, int index, const char *name, long def)
-{
-    (void) name;
-
-    // use default?
-    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
-        return def;
-   
-    // conver to integer
-    // XXX: check compatibility?
-    return lua_tointeger(L, index);
-}
-
-static void * _lua_arg_obj (lua_State *L, int index, const struct lua_type *type, bool optional)
-{
-    // not given?
-    if (!lua_isnoneornil(L, index))
-        return lua_type_get(L, type, index);
-
-    if (optional)
-        return NULL;
-    
-    luaL_error(L, "missing value for required object argument <%d:%s>", index, type->name);
-    return NULL;
-}
-
-/**
- * Look up the arg index to use for the given index/name.
- *
- * If no value is found for the corresponding index, returns zero.
- */
-static int lua_arg_index (lua_State *L, int nargs, int index, const char *name)
-{
-    // lookup from table?
-    if (nargs == 2 && lua_istable(L, 2) && name) {
-        // push the value from the named field onto the stack
-        lua_getfield(L, 2, name);
-        
-        // no named field?
-        if (lua_isnil(L, -1)) {
-            lua_pop(L, 1);
-
-            lua_getindex(L, 2, index - 1);
-        }
-
-        // no index field?
-        if (lua_isnil(L, -1)) {
-            lua_pop(L, 1);
-
-            return 0;
-        }
-        
-        // found either a named or indexed arg
-        return lua_gettop(L);
-
-    } else if (index <= nargs) {
-        // use the same index
-        return index;
-
-    } else {
-        // no index
-        return 0;
-    }
-}
-
-const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def)
-{
-    return _lua_arg_string(L, lua_arg_index(L, nargs, index, name), name, def);
-}
-
-bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def)
-{
-    return _lua_arg_bool(L, lua_arg_index(L, nargs, index, name), name, def);    
-}
-
-void* lua_arg_obj (lua_State *L, int nargs, int index, const struct lua_type *type, bool optional)
-{
-    return _lua_arg_obj(L, lua_arg_index(L, nargs, index, NULL), type, optional);
-}
-
-long lua_arg_int (lua_State *L, int nargs, int index, const char *name, long def)
-{
-    return _lua_arg_int(L, lua_arg_index(L, nargs, index, name), name, def);
-}
-
-void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...)
-{
-    int argidx = 1, argtbl = 0, idx;
-    const struct lua_func_arg *arg;
-    va_list vargs;
-
-    // first, the obj argument
-    if (func->type)
-        *obj_ptr = lua_type_get(L, func->type, argidx++);
-
-    // were we given a table of arguments?
-    if (lua_istable(L, argidx))
-        argtbl = argidx++;
-
-    // parse the args
-    va_start(vargs, obj_ptr);
-
-    for (arg = func->args, idx = 1; arg->name && arg->type; arg++, idx++) {
-        int index;
-
-        // map index
-        if (!argtbl)
-            // direct
-            index = argidx++;
-        
-        else
-            // lookup from table
-            index = lua_arg_lookup(L, argtbl, arg->name, idx);
-        
-        // apply
-        switch (arg->type) {
-            case LUA_ARG_STRING:
-                *va_arg(vargs, const char **) = _lua_arg_string(L, index, arg->name, arg->def.string);
-                break;
-
-            case LUA_ARG_BOOL:
-                *va_arg(vargs, bool *) = _lua_arg_bool(L, index, arg->name, arg->def.boolean);
-                break;
-            
-            case LUA_ARG_INT:
-                *va_arg(vargs, long *) = _lua_arg_int(L, index, arg->name, arg->def.integer);
-                break;
-
-            default:
-                NOT_REACHED();
-        };
-    }
-
-    va_end(vargs);
-}
--- a/src/lua_func.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-#ifndef LUA_FUNC_H
-#define LUA_FUNC_H
-
-/**
- * @file
- *
- * Convenience functions for working with lua C functions
- */
-#include "lua_type.h"
-#include <stdbool.h>
-
-/**
- * Lua function argument types
- */
-enum lua_arg_type {
-    LUA_ARG_INVALID,
-    
-    /** A `const char *` pointing to lua-GC'd memory */
-    LUA_ARG_STRING,
-
-    /** A c99 `bool` */
-    LUA_ARG_BOOL,
-
-    /** A `long signed int` */
-    LUA_ARG_INT,
-};
-
-/**
- * Function argument def
- */
-struct lua_func_arg {
-    /** Argument name */
-    const char *name;
-
-    /** Expected type */
-    enum lua_arg_type type;
-
-    /** Default value */
-    union {
-        const char *string;
-        int boolean;
-        long integer;
-    } def;
-};
-
-/**
- * Function def
- */
-struct lua_func {
-    /** Object type, or NULL */
-    const struct lua_type *type;
-
-    /** Function name */
-    const char *name;
-
-    /** Help string */
-    const char *help;
-
-    /** Arguments */
-    const struct lua_func_arg args[];
-};
-
-/**
- * Used as the "invalid" default value
- */
-#define LUA_ARG_REQUIRED (-1)
-#define LUA_ARG_STRING_REQUIRED ((const char *) (-1))
-
-/**
- * Define a function argument
- */
-#define LUA_FUNC_ARG_STRING(name, def) { (name), LUA_ARG_STRING, { .string = (def) } }
-#define LUA_FUNC_ARG_BOOL(name, def) { (name), LUA_ARG_BOOL, { .boolean = (def) } }
-#define LUA_FUNC_ARG_INT(name, def) { (name), LUA_ARG_INT, { .integer = (def) } }
-#define LUA_FUNC_ARG_END { NULL, 0, { 0 } }
-
-/**
- * Define a function
- */
-#define LUA_FUNC(type, name, help, ...) { (type), (name), (help), { __VA_ARGS__, LUA_FUNC_ARG_END } }
-
-/**
- * Parse and return a string argument
- */
-const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def);
-
-/**
- * Parse and return a boolean argument
- */
-bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def);
-
-/**
- * Parse and return an integer argument
- */
-long lua_arg_int (lua_State *L, int nargs, int index, const char *name, long def);
-
-/**
- * Return a userdata argument at the given fixed index
- */
-void* lua_arg_obj (lua_State *L, int nargs, int index, const struct lua_type *type, bool optional);
-
-/**
- * Parse function arguments as defined
- */
-void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...);
-
-#endif
--- a/src/lua_irc.h	Thu May 28 00:35:02 2009 +0300
+++ b/src/lua_irc.h	Thu May 28 01:17:36 2009 +0300
@@ -4,10 +4,11 @@
 /**
  * Defines lua wrappers for the irc_* objects
  */
-#include "nexus_lua.h"
-#include "lua_objs.h"
 #include "irc_client.h"
 
+// XXX: remove this dep ASAP.
+#include <spbot/nexus_lua.h>
+
 /**
  * Our lua wrapper for irc_chan
  */
--- a/src/lua_objs.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,462 +0,0 @@
-#include "lua_objs.h"
-#include "lua_irc.h"
-#include "lua_func.h"
-#include "lua_thread.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-/**
- * Wrapper for module
- */
-struct lua_module {
-    struct module *module;
-};
-
-static struct lua_type lua_module_type = LUA_TYPE("spbot.module");
-
-/**
- * Create a lua_module userdata from the given module and push it onto the stack, returning 1.
- *
- * The given module should be a reference of its own right.
- */
-static int lua_module_create (lua_State *L, struct module *module)
-{
-    // create the new obj
-    struct lua_module *lua_module = lua_type_create(L, &lua_module_type, sizeof(*lua_module));
-
-    // initialize
-    lua_module->module = module;
-
-    // ok
-    return 1;
-}
-
-/**
- * module_put() our module reference
- */
-static int lua_module__gc (lua_State *L)
-{
-    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
-    
-    // put it
-    module_put(lua_module->module);
-
-    return 0;
-}
-
-static int lua_module_conf (lua_State *L)
-{
-    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
-    const struct config_option *option;
-    struct error_info err;
-    bool is_err = true;
-
-    // the list of given config values, and temporary storage for string values
-    struct config_value values[CONFIG_VALUES_MAX], *value = values;
-    char *value_bufs[CONFIG_VALUES_MAX], **value_buf = value_bufs;
-
-    // number of arguments given
-    int nargs = lua_gettop(L), argidx = 2;
-
-    // init to zero
-    memset(values, 0, sizeof(values));
-    memset(value_bufs, 0, sizeof(value_bufs));
-
-    // XXX: come up with some better way...
-    struct nexus *nexus = lua_module->module->modules->nexus;
-
-    // the config name
-    const char *conf_name = luaL_checkstring(L, argidx++);
-
-    // look it up
-    if ((option = module_conf_lookup(lua_module->module, conf_name, &err)) == NULL)
-        return luaL_error(L, "module_conf_lookup: %s/%s: %s", module_name(lua_module->module), conf_name, error_msg(&err));
-
-    // maximum number of arguments accepted
-    int maxargs = config_params_count(option);
-
-    // too many arguments?
-    if (nargs - argidx > maxargs)
-        return luaL_error(L, "lua_module_conf: too many arguments (>%d) given (%d)", maxargs, nargs - argidx);
-    
-    // the current param
-    const struct config_param *param = option->params;
-
-    // apply each given argument to the correct param, storing it in value
-    for (; argidx <= nargs; argidx++, value++, param++) {
-        // the given config value
-        switch (lua_type(L, argidx)) {
-            case LUA_TNONE:
-            case LUA_TNIL:
-                // no value
-                value->type = CONFIG_NULL;
-
-                break;
-
-            case LUA_TSTRING: {
-                // string arg
-                const char *arg_str = lua_tostring(L, argidx);
-
-                // copy it as a mutable string buffer
-                if ((*value_buf = strdup(arg_str)) == NULL) {
-                    lua_pushfstring(L, "strdup");
-                    goto error;
-                }
-
-                // parse it as a raw value
-                if (config_parse_param(param, nexus, value, *value_buf, &err)) {
-                    lua_pushfstring(L, "config_parse: %s/%s: %s", option->name, *value_buf, error_msg(&err));
-                    goto error;
-                }
-
-                // seek to next value_buf
-                value_buf++;
-
-            } break;
-    
-            case LUA_TUSERDATA:
-                // some kind of userdata, use its metatable to figure out what type it is
-                if (!lua_getmetatable(L, argidx)) {
-                    lua_pushfstring(L, "config value is userdata without metatable");
-                    goto error;
-                }
-
-                // get the target metatable
-                lua_getfield(L, LUA_REGISTRYINDEX, "evirc.chan");
-
-                // is it a chan?
-                if (!lua_rawequal(L, -1, -2)) {
-                    lua_pushfstring(L, "config value is userdata of unknown type");
-                    goto error;
-                }
-
-                // pop the metatables
-                lua_pop(L, 2);
-
-                // get the irc_chan
-                struct lua_chan *lua_chan = lua_touserdata(L, argidx);
-
-                // build the value
-                value->type = CONFIG_IRC_CHAN;
-                value->irc_chan = lua_chan->chan;
-                
-                break;
-            
-            default:
-                lua_pushfstring(L, "config value is of unknown lua type '%s'", lua_typename(L, argidx));
-                goto error;
-
-        }
-    }
-
-    // apply it
-    if (module_conf(lua_module->module, option, values, &err)) {
-        lua_pushfstring(L, "module_conf: %s/%s: %s", module_name(lua_module->module), option->name, error_msg(&err));
-        goto error;
-    }
-    
-    // ok
-    is_err = false;
-
-error:
-    // release any allocated strings
-    for (value_buf = value_bufs; value_buf <= value_bufs + CONFIG_VALUES_MAX && *value_buf; value_buf++)
-        free(*value_buf);
-    
-    // either error or successful return
-    if (is_err)
-        return lua_error(L);
-    else
-        return 0;
-}
-
-static int lua_module_unload (lua_State *L)
-{
-    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
-    struct error_info err;
-
-    // just unload it
-    if ((ERROR_CODE(&err) = module_unload(lua_module->module)))
-        return luaL_error(L, "module_unload: %s: %s", module_name(lua_module->module), error_msg(&err));
-
-    // ok
-    return 0;
-}
-
-static struct lua_method lua_module_methods[] = LUA_METHODS(
-        LUA_METHOD("__gc",      lua_module__gc,     NULL    ),
-        LUA_METHOD("conf",      lua_module_conf,    NULL    ),
-        LUA_METHOD("unload",    lua_module_unload,  NULL    )
-);
-
-/**
- * Wrapper for modules
- */
-struct lua_modules {
-    struct modules *modules;
-    
-    // strdup'd path for module_path
-    // XXX: remove when gc'd
-    char *path;
-};
-
-static struct lua_type lua_modules_type = LUA_TYPE("spbot.modules");
-
-static int lua_modules__gc (lua_State *L)
-{
-    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
-    
-    // remove the modules path if it was set by us
-    if (lua_modules->path && modules_path(lua_modules->modules, NULL) == lua_modules->path)
-        modules_path(lua_modules->modules, "");
-
-    // release any strdup'd path
-    free(lua_modules->path);
-
-    // ok
-    return 0;
-}
-
-static int lua_modules_path (lua_State *L)
-{
-    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
-    char *path = NULL;
-    const char *old_path;
-    
-    if (!lua_isnoneornil(L, 2)) {
-        // the new path
-        if ((path = strdup(luaL_checkstring(L, 2))) == NULL)
-            return luaL_error(L, "strdup");
-    }
-
-    // set or get
-    old_path = modules_path(lua_modules->modules, path);
-
-    // return the old path
-    if (old_path)
-        lua_pushstring(L, old_path);
-    else
-        lua_pushnil(L);
-
-    if (path) {
-        // replace the old path
-        free(lua_modules->path); 
-        lua_modules->path = path;
-    }
-
-    // ok
-    return 1;
-}
-
-static int lua_modules_load (lua_State *L)
-{
-    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
-    struct module *module;
-    struct module_info info;
-    struct error_info err;
-
-    // the module name/path
-    info.name = luaL_checkstring(L, 2);
-    info.path = lua_isnoneornil(L, 3) ? NULL : luaL_checkstring(L, 3);
-
-    // load and get a new reference
-    if (module_load(lua_modules->modules, &module, &info, &err))
-        return luaL_error(L, "module_load: %s/%s: %s", info.name, info.path, error_msg(&err));
-
-    // wrap
-    return lua_module_create(L, module);
-}
-
-static int lua_modules_module (lua_State *L)
-{
-    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
-    struct module *module;
-    
-    // the module name
-    const char *name = luaL_checkstring(L, 2);
-
-    // look it up, as a new reference
-    if ((module = modules_get(lua_modules->modules, name)) == NULL)
-        return luaL_error(L, "module_get: %s: no such module", name);
-
-    // wrap
-    return lua_module_create(L, module);
-}
-
-static struct lua_method lua_modules_methods[] = LUA_METHODS(
-        LUA_METHOD("__gc",      lua_modules__gc,    NULL    ),
-        LUA_METHOD("path",      lua_modules_path,   NULL    ),
-        LUA_METHOD("load",      lua_modules_load,   NULL    ),
-        LUA_METHOD("module",    lua_modules_module, NULL    )
-    );
-
-
-
-/**
- * Initialize the spbot.modules type for lua_modules, and registers an instance bound to the given modules list at
- * 'modules'.
- */
-static void lua_modules_init (lua_State *L, struct modules *modules)
-{
-    // allocate the global "modules" object
-    struct lua_modules *lua_modules = lua_type_register_global(L, &lua_modules_type, lua_modules_methods, "modules", sizeof(*lua_modules));
-    
-    // initialize it
-    lua_modules->modules = modules;
-}
-
-/**
- * Wrapper for nexus
- */
-struct lua_nexus {
-    struct nexus *nexus;
-};
-
-static struct lua_type lua_nexus_type = LUA_TYPE("spbot.nexus");
-
-static int lua_nexus_shutdown (lua_State *L)
-{
-    struct lua_nexus *lua_nexus = lua_type_get(L, &lua_nexus_type, 1);
-
-    // just shut it down
-    nexus_shutdown(lua_nexus->nexus);
-
-    return 0;
-}
-
-static int lua_nexus_load_config (lua_State *L)
-{
-    struct lua_nexus *lua_nexus = lua_type_get(L, &lua_nexus_type, 1);
-    struct error_info err;
-
-    const char *path = luaL_checkstring(L, 2);
-    
-    // just load it
-    if (nexus_load_config(lua_nexus->nexus, path, &err))
-        return luaL_error(L, "nexus_load_config(%s): %s", path, error_msg(&err));
-
-    return 0;
-}
-
-static struct lua_func lua_nexus_sleep_func = LUA_FUNC(&lua_nexus_type, "sleep",
-        "Schedules itself to resume after the given delay (in seconds) and yields",
-
-        LUA_FUNC_ARG_INT("tv_sec",  LUA_ARG_REQUIRED)
-    );
-
-static void lua_nexus_sleep_wakeup (evutil_socket_t fd, short what, void *arg)
-{
-    lua_State *L = arg;
-
-    (void) fd;
-    (void) what;
-
-    // resume the thread that called lua_nexus_sleep
-    lua_thread_resume_state(L);
-}
-
-static int lua_nexus_sleep (lua_State *L)
-{
-    struct lua_nexus *lua_nexus;
-    long tv_sec;
-
-    // parse args
-    lua_args_parse(L, &lua_nexus_sleep_func, (void *) &lua_nexus, &tv_sec);
-
-    // build tv
-    struct timeval tv = { tv_sec, 0 };
-    
-    // schedule wakeup
-    // use a pure-timeout event
-    if (event_base_once(lua_nexus->nexus->ev_base, -1, EV_TIMEOUT, lua_nexus_sleep_wakeup, L, &tv))
-        return luaL_error(L, "event_base_once");
-
-    // yield
-    return lua_thread_yield_state(L);
-}
-
-static struct lua_method lua_nexus_methods[] = LUA_METHODS(
-        LUA_METHOD("shutdown",      lua_nexus_shutdown,     NULL                    ),
-        LUA_METHOD("load_config",   lua_nexus_load_config,  NULL                    ),
-        LUA_METHOD("sleep",         lua_nexus_sleep,        &lua_nexus_sleep_func   )
-    );
-
-/**
- * Initialize the spbot.nexus type for lua_nexus, and registers an instance bound to the given nexus list at
- * 'nexus'.
- */
-static void lua_nexus_init (lua_State *L, struct nexus *nexus)
-{
-    // allocate the global "nexus" object
-    struct lua_nexus *lua_nexus = lua_type_register_global(L, &lua_nexus_type, lua_nexus_methods, "nexus", sizeof(*lua_nexus));
-    
-    // initialize it
-    lua_nexus->nexus = nexus;
-}
-
-
-/**
- * Global functions
- */
-static int lua_log_level (lua_State *L)
-{
-    // log level as a string
-    enum log_level new_level = luaL_checkoption(L, 1, NULL, log_level_names);
-
-    // set it
-    set_log_level(new_level);
-
-    // ok
-    return 0;
-}
-
-static int lua_log (lua_State *L)
-{
-    // log level as a string
-    enum log_level level = luaL_checkoption(L, 1, NULL, log_level_names);
-    
-    // log message
-    const char *msg = luaL_checkstring(L, 2);
-
-    // log it
-    _log_msg(level, "lua", "%s", msg);
-
-    // ok
-    return 0;
-}
-
-static const struct luaL_Reg lua_global_functions[] = {
-    {   "log_level",    lua_log_level               },
-    {   "log",          lua_log                     },
-    {   NULL,           NULL                        }
-};
-
-static void lua_global_init (lua_State *L)
-{
-    const struct luaL_Reg *reg;
-
-    for (reg = lua_global_functions; reg->name && reg->func; reg++) {
-        // put the function on the stack
-        lua_pushcfunction(L, reg->func);
-
-        // set the global
-        lua_setglobal(L, reg->name);
-    }
-}
-
-void lua_objs_init (struct nexus_lua *lua)
-{
-    // register types
-    lua_type_register(lua->st, &lua_module_type, lua_module_methods);
-    
-    // globals
-    lua_nexus_init(lua->st, lua->nexus);
-    lua_modules_init(lua->st, lua->nexus->modules);
-    
-    // global functions
-    lua_global_init(lua->st);
-}
-
-
--- a/src/lua_objs.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#ifndef LUA_OBJS_H
-#define LUA_OBJS_H
-
-/**
- * @file
- *
- * Defines lua functions to access the various objects in a nexus
- */
-#include "nexus_lua.h"
-
-#include <lua5.1/lua.h>
-#include <lua5.1/lauxlib.h>
-
-// XXX: remove
-#include "lua_type.h"
-
-/**
- * Registers our lua runtime objects into the given lua state.
- *
- * Call in protected mode.
- */
-void lua_objs_init (struct nexus_lua *lua);
-
-#endif /* LUA_OBJS_H */
--- a/src/lua_type.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-#include "lua_type.h"
-
-#include <lua5.1/lauxlib.h>
-
-void lua_type_register (lua_State *L, const struct lua_type *type, const struct lua_method methods[])
-{
-    const struct lua_method *method;
-
-    // create the metatable
-    luaL_newmetatable(L, type->name);
-
-    // set the metatable __index to itself
-    lua_pushvalue(L, -1);
-    lua_setfield(L, -1, "__index");
-
-    // add the methods to the metatable
-    for (method = methods; method->func; method++) {
-        lua_pushcfunction(L, method->func);
-        lua_setfield(L, -2, method->name);
-    }
-}
-
-void* lua_type_create (lua_State *L, const struct lua_type *type, size_t size)
-{
-    // create the new userdata on the stack
-    void *ud = lua_newuserdata(L, size);
-
-    // get the type and set it
-    luaL_getmetatable(L, type->name);
-    lua_setmetatable(L, -2);
-
-    // ok
-    return ud;
-}
-
-void* lua_type_register_global (lua_State *L, const struct lua_type *type, const struct lua_method methods[],
-        const char *global_name, size_t size)
-{
-    // allocate the global object
-    void *obj = lua_newuserdata(L, size);
-    
-    // create the type metatable
-    lua_type_register(L, type, methods);
-
-    // set the userdata's metatable
-    lua_setmetatable(L, -2);
-
-    // store it as a global
-    lua_setglobal(L, global_name);
-    
-    // ok
-    return obj;
-}
-
-void* lua_type_get (lua_State *L, const struct lua_type *type, int index)
-{
-    void *ud;
-
-    // validate the userdata arg
-    // XXX: the luaL_checkudata actually raises an error itself
-    if ((ud = luaL_checkudata(L, index, type->name)) == NULL) {
-        luaL_error(L, "bad type argument: `%s` expected", type->name); return NULL;
-
-    } else {
-        // ok
-        return ud;
-
-    }
-}
--- a/src/lua_type.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-#ifndef LUA_TYPE_H
-#define LUA_TYPE_H
-
-/**
- * @file
- *
- * Convenience functions for defining "types" in lua
- */
-#include <lua5.1/lua.h>
-
-// XXX: remove
-#include <lua5.1/lauxlib.h>
-
-/**
- * A type's method
- *
- * XXX: a name field?
- */
-struct lua_method {
-    /** The name of the method */
-    const char *name;
-
-    /** The function pointer */
-    lua_CFunction func;
-
-    /** The function definition, optional */
-    const struct lua_func *info;
-};
-
-#define LUA_METHOD(name, func, info) \
-    { (name), (func), (info) }
-
-#define LUA_METHODS(...) \
-    { __VA_ARGS__, { NULL, NULL, NULL } }
-
-/**
- * A type
- */
-struct lua_type {
-    /** The name of the type */
-    const char *name;
-};
-
-#define LUA_TYPE(name) \
-    { (name) }
-
-/**
- * Register a new metadata table for the given type in the given lua state.
- *
- * This leaves the new type (metatable) on the stack.
- */
-void lua_type_register (lua_State *L, const struct lua_type *type, const struct lua_method methods[]);
-
-/**
- * Create a new instance of the given type.
- *
- * This leaves the new userdata object on the stack.
- */
-void* lua_type_create (lua_State *L, const struct lua_type *type, size_t size);
-
-/**
- * Create a new userdata type, and also create an instance of it, register it as a global, and return it.
- *
- * This leaves the new userdata object on the stack.
- */
-void* lua_type_register_global (lua_State *L, const struct lua_type *type, const struct lua_method methods[],
-        const char *global_name, size_t size);
-
-/**
- * Get an object of the given type from the given stack position
- */
-void* lua_type_get (lua_State *L, const struct lua_type *type, int index);
-
-#endif
--- a/src/msg_proto.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,493 +0,0 @@
-#include "msg_proto.h"
-
-#include <string.h>
-#include <stdint.h>
-#include <arpa/inet.h>
-
-/**
- * I/O buffer
- */
-struct msg_buf {
-    /** Buffer base pointer */
-    char *base;
-
-    /** Size of the buffer */
-    size_t size;
-
-    /** Current read/write offset */
-    size_t off;
-};
-
-/**
- * The minimum size used for any msg_buf::size related operation.
- */
-#define MSG_BUF_MIN_SIZE 1024
-
-/**
- * Growth rate for size
- */
-#define MSG_BUF_GROW_RATE 2
-
-/**
- * Initialize a message buffer at the given initial size
- */
-err_t msg_buf_init (struct msg_buf *buf, size_t hint)
-{
-    // apply minimum size
-    if (hint < MSG_BUF_MIN_SIZE)
-        hint = MSG_BUF_MIN_SIZE;
-
-    // allocate the initial buffer
-    if ((buf->base = malloc(hint)) == NULL)
-        return ERR_MEM;
-    
-    // set fields
-    buf->size = hint;
-    buf->off = 0;
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Grow the buffer if needed to fit the given capacity.
- */
-err_t msg_buf_grow (struct msg_buf *buf, size_t size)
-{
-    char *tmp = buf->base;
-
-    if (buf->size >= size)
-        // nothing to do
-        return SUCCESS;
-
-    // calculate new size
-    while (buf->size < size)
-        buf->size *= MSG_BUF_GROW_RATE;
-
-    // resize
-    if ((buf->base = realloc(buf->base, buf->size)) == NULL) {
-        buf->base = tmp;
-
-        return ERR_MEM;
-    }
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Drain \a len bytes off the head of the buffer
- */
-err_t msg_buf_drain (struct msg_buf *buf, size_t len)
-{
-    // simple memmove
-    memmove(buf->base, buf->base + len, buf->off - len);
-
-    // update offfset
-    buf->off -= len;
-    
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Read into the buffer from a transport_t.
- *
- * This will attempt to read \a len bytes onto the end of the buffer, growing it if needed to fit.
- *
- * This may read/return more data than the given len. Use msg_buf_drain the remove the data from the buffer once you
- * have used it.
- *
- * Returns the number of new bytes read, zero for transport read buffer empty, -err_t for error.
- */
-ssize_t msg_buf_read (struct msg_buf *buf, transport_t *transport, size_t len, error_t *err)
-{
-    ssize_t ret;
-
-    // clamp size
-    if (len < MSG_BUF_MIN_SIZE)
-        len = MSG_BUF_MIN_SIZE;
-
-    // ensure space
-    if ((ERROR_CODE(err) = msg_buf_grow(buf, buf->off + len)))
-        goto error;
-
-    // read
-    if ((ret = transport_read(transport, buf->base + buf->off, len, err)) < 0)
-        goto error;
-    
-    // no data left?
-    if (!ret)
-        return 0;
-
-    // update offset
-    buf->off += ret;
-
-    // ok
-    return ret;
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-/**
- * Drives transport_write on the given data until all the given data is written, or zero is returned.
- *
- * @param transport transport to write to
- * @param data input data
- * @param len number of bytes to write from data
- * @param err returned error info
- * @return number of bytes written (which may be zero or less than len), or -err_t.
- */
-static ssize_t _transport_write_all (transport_t *transport, const char *data, size_t len, error_t *err)
-{
-    ssize_t ret;
-    size_t written = 0;
-
-    while (len) {
-        // try and write out remaining data
-        if ((ret = transport_write(transport, data, len, err)) < 0)
-            goto error;
-        
-        if (!ret) {
-            // write buffer full
-            break;
-
-        } else { 
-            // update and continue
-            written += ret;
-            data += ret;
-            len -= ret;
-        }
-    }
-
-    // ok
-    return written;
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-/**
- * If the buffer is empty, this will attempt to write the given data directly using transport_write until either all
- * the data is written (in which case nothing more needs to be done), or the transport won't accept any more writes,
- * in which case the remaining data will be buffered.
- *
- * If the buffer is not empty, then the given data will be added to the end of the buffer, since otherwise the order of
- * data would be broken.
- *
- * In either case, transport_write semantics garuntee that our buffer will either be empty, or an on_write will be
- * pending on the transport. See msg_buf_flush() for how to handle transport_callbacks::on_write.
- */
-err_t msg_buf_write (struct msg_buf *buf, transport_t *transport, const void *data_ptr, size_t len, error_t *err)
-{
-    ssize_t ret;
-    const char *data = data_ptr;
-
-    if (!buf->off) {
-        // no data buffered, so we can try and write directly
-        if ((ret = _transport_write_all(transport, data, len, err)) < 0)
-            goto error;
-    
-        // update written
-        data += ret;
-        len -= ret;
-        
-        if (len == 0)
-            // wrote it all
-            return SUCCESS;
-    }
-
-    // ensure space
-    if ((ERROR_CODE(err) = msg_buf_grow(buf, buf->off + len)))
-        goto error;
-
-    // store
-    memcpy(buf->base + buf->off, data, len);
-    
-    // update
-    buf->off += len;
-
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(err);    
-}
-
-/**
- * Flush buffered write data to the transport, driving transport_write() until either all of our bufferd data has been
- * written, or the transport will not accept any more.
- *
- * In either case, transport_write semantics garuntee that our buffer will either be empty, or an on_write will be
- * pending on the transport.
- */
-err_t msg_buf_flush (struct msg_buf *buf, transport_t *transport, error_t *err)
-{
-    ssize_t ret;
-
-    // write
-    if ((ret = _transport_write_all(transport, buf->base, buf->off, err)) < 0)
-        goto error;
-    
-    if (ret)
-        // unbuffer the written data
-        msg_buf_drain(buf, ret);
-
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(err);    
-}
-
-/**
- * Deinitialize msg_buf to release allocated buffers
- */
-void msg_buf_deinit (struct msg_buf *buf)
-{
-    // release
-    free(buf->base);
-
-    // reset
-    buf->base = NULL;
-    buf->size = buf->off = 0;
-}
-
-/**
- * Message header
- */
-struct msg_header {
-    /** Message length, including header */
-    uint16_t len;
-};
-
-/**
- * Message header size
- */
-#define MSG_PROTO_HEADER_SIZE (sizeof(uint16_t))
-
-/**
- * Our state struct
- */
-struct msg_proto {
-    /** The transport */
-    transport_t *transport;
-
-    /** User callbacks */
-    const struct msg_proto_callbacks *cb_tbl;
-
-    /** User callback argument */
-    void *cb_arg;
-
-    /** Input buffer */
-    struct msg_buf in;
-
-    /** Output buffer */
-    struct msg_buf out;
-};
-
-/**
- * Signal error to user
- */
-static void msg_proto_error (struct msg_proto *proto, const error_t *err)
-{
-    // invoke user callback
-    proto->cb_tbl->on_error(proto, err, proto->cb_arg);
-}
-
-/**
- * Attempt to read the current header from our input buffer.
- *
- * Returns >0 for full header, 0 for incomplete header, -err_t for error.
- */
-static int msg_proto_peek_header (struct msg_proto *proto, struct msg_header *header, error_t *err)
-{
-    if (proto->in.off < MSG_PROTO_HEADER_SIZE)
-        // not enough data for header
-        return 0;
-
-    // read header
-    header->len = ntohs(*((uint16_t *) proto->in.base));
-
-    // bad header?
-    if (header->len < MSG_PROTO_HEADER_SIZE)
-        JUMP_SET_ERROR_STR(err, ERR_MISC, "message_header::len");
-
-    // ok, got header
-    return 1;
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-/**
- * Recieved a message with the given header, and a pointer to the message data
- *
- * XXX: what to do if the user callback destroys the msg_proto?
- */
-static err_t msg_proto_on_msg (struct msg_proto *proto, struct msg_header *header, char *data, error_t *err)
-{
-    (void) err;
-
-    // invoke user callback
-    proto->cb_tbl->on_msg(proto, data, header->len - MSG_PROTO_HEADER_SIZE, proto->cb_arg);
-
-    // XXX: handle user errors
-    return SUCCESS;
-}
-
-static void msg_proto_on_read (transport_t *transport, void *arg)
-{
-    struct msg_proto *proto = arg;
-    struct msg_header header;
-    ssize_t ret;
-    error_t err;
-    
-    // we might be able to read more than one message per event
-    do {
-        // try and read message length for incomplete message
-        if ((ret = msg_proto_peek_header(proto, &header, &err)) < 0)
-            goto error;
-        
-        // need to read more data?
-        if (!ret || header.len > proto->in.off) {
-            // msg_buf_read a minimum size, so passing a zero is OK
-            size_t to_read = ret ? header.len : 0;
-
-            // read into our buffer
-            if ((ret = msg_buf_read(&proto->in, transport, to_read, &err)) < 0)
-                goto error;
-    
-        } else {
-            // handle full message
-            if (msg_proto_on_msg(proto, &header, proto->in.base + MSG_PROTO_HEADER_SIZE, &err))
-                goto error;
-            
-            // remove the data from the buffer
-            msg_buf_drain(&proto->in, header.len);
-        }
-    } while (ret);
-    
-    // ok
-    return;
-
-error:
-    // notify user
-    msg_proto_error(proto, &err);    
-}
-
-static void msg_proto_on_write (transport_t *transport, void *arg)
-{
-    struct msg_proto *proto = arg;
-    error_t err;
-
-    // flush
-    if (msg_buf_flush(&proto->out, transport, &err))
-        // notify user on transport errors
-        msg_proto_error(proto, &err);
-}
-
-static void msg_proto_on_error (transport_t *transport, const error_t *err, void *arg)
-{
-    struct msg_proto *proto = arg;
-
-    (void) transport;
-
-    // report to user
-    msg_proto_error(proto, err);
-}
-
-static const struct transport_callbacks msg_proto_transport_callbacks = {
-    .on_read    = msg_proto_on_read,
-    .on_write   = msg_proto_on_write,
-    .on_error   = msg_proto_on_error,
-};
-
-err_t msg_proto_create (struct msg_proto **proto_ptr, transport_t *transport, const struct msg_proto_callbacks *cb_tbl, void *cb_arg, error_t *err)
-{
-    struct msg_proto *proto;
-
-    // alloc
-    if ((proto = calloc(1, sizeof(*proto))) == NULL)
-        return ERR_MEM;
-
-    // store
-    proto->transport = transport;
-    proto->cb_tbl = cb_tbl;
-    proto->cb_arg = cb_arg;
-
-    // init
-    if (
-            (ERROR_CODE(err) = msg_buf_init(&proto->in, 0))
-        ||  (ERROR_CODE(err) = msg_buf_init(&proto->out, 0))
-    )
-        goto error;
-
-    // setup transport
-    if ((ERROR_CODE(err) = transport_events(transport, TRANSPORT_READ | TRANSPORT_WRITE)))
-        goto error;
-
-    transport_set_callbacks(transport, &msg_proto_transport_callbacks, proto);
-
-    // ok
-    *proto_ptr = proto;
-
-    return SUCCESS;
-
-error:
-    // release
-    msg_proto_destroy(proto);
-
-    return ERROR_CODE(err);
-}
-
-/**
- * Build and write out the data for the given header
- */
-static err_t msg_proto_write_header (struct msg_proto *proto, const struct msg_header *header, error_t *err)
-{
-    char buf[MSG_PROTO_HEADER_SIZE];
-
-    // validate
-    if (header->len < MSG_PROTO_HEADER_SIZE)
-        return SET_ERROR(err, ERR_MISC);
-
-    // build
-    *((uint16_t *) buf) = htons(header->len);
-
-    // write
-    return msg_buf_write(&proto->out, proto->transport, buf, sizeof(buf), err);
-}
-
-err_t msg_proto_send (struct msg_proto *proto, const void *data, size_t len, error_t *err)
-{
-    struct msg_header header;
-
-    // build header
-    header.len = MSG_PROTO_HEADER_SIZE + len;
-
-    // write it
-    if (
-            msg_proto_write_header(proto, &header, err)
-        ||  msg_buf_write(&proto->out, proto->transport, data, len, err)
-    )
-        return ERROR_CODE(err);
-
-    // ok
-    return SUCCESS;
-}
-
-void msg_proto_destroy (struct msg_proto *proto)
-{
-    // drop buffers
-    msg_buf_deinit(&proto->in);
-    msg_buf_deinit(&proto->out);
-
-    // kill transport
-    transport_destroy(proto->transport);
-
-    // release ourself
-    free(proto);
-}
-
--- a/src/msg_proto.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#ifndef MSG_PROTO_H
-#define MSG_PROTO_H
-
-/**
- * @param
- *
- * Support for simple protocols that send/recieve length-prefixed messages over a transport stream.
- *
- * This implementation is mostly geared towards handling a reasonable number of reasonably sized messages in a
- * reasonable way. Hence, 
- */
-#include "transport.h"
-
-/** 
- * Protocol state struct
- */
-struct msg_proto;
-
-/**
- * User callbacks
- */
-struct msg_proto_callbacks {
-    /**
-     * Message recieved.
-     *
-     * XXX: currently you must not call msg_proto_destroy from within this callback
-     */
-    void (*on_msg) (struct msg_proto *proto, void *data, size_t len, void *arg);
-
-    /**
-     * Transport/protocol error occured in event handling.
-     */
-    void (*on_error) (struct msg_proto *proto, const error_t *err, void *arg);
-};
-
-/**
- * Create a msg_proto state using the given transport.
- *
- * This will install our callback handlers on the given transport.
- */
-err_t msg_proto_create (struct msg_proto **proto_ptr, transport_t *transport, const struct msg_proto_callbacks *cb_tbl, void *cb_arg, error_t *err);
-
-/**
- * Send a message to the other endpoint
- */
-err_t msg_proto_send (struct msg_proto *proto, const void *data, size_t len, error_t *err);
-
-/**
- * Destroy the protocol state and transport
- */
-void msg_proto_destroy (struct msg_proto *proto);
-
-
-#endif /* MSG_PROTO_H */
--- a/src/resolve.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-#include "resolve.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <stdio.h>
-
-err_t resolve_addr (struct resolve_result *res, const char *node, const char *service, int socktype, int ai_flags, error_t *err)
-{
-    struct addrinfo hints, *ai;
-    int ret;
-
-    // build hints
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_flags = ai_flags;
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = socktype;
-
-    // resolve (blocking)
-    if ((ret = getaddrinfo(node, service, &hints, &ai)))
-        RETURN_SET_ERROR_EXTRA(err, ERR_GETADDRINFO, ret);
-
-    // no results?
-    if (!ai)
-        RETURN_SET_ERROR(err, ERR_GETADDRINFO_EMPTY);
-    
-    // store
-    res->list = res->item = ai;
-
-    // ok
-    return SUCCESS;
-}
-
-void resolve_result_init (struct resolve_result *res)
-{
-    res->list = res->item = NULL;
-}
-
-struct addrinfo* resolve_result_next (struct resolve_result *res)
-{
-    if (!res->item) {
-        // no items left
-        return NULL;
-
-    } else {
-        // ...remember the current item
-        struct addrinfo *ai = res->item;
-
-        if (res->item)
-            // advance item to the next one
-            res->item = res->item->ai_next;
-
-        // return the current one
-        return ai;
-    }
-}
-
-void resolve_result_deinit (struct resolve_result *res)
-{
-    if (res->list)
-        // free them all
-        freeaddrinfo(res->list);
-    
-    // invalidate
-    res->list = res->item = NULL;
-}
-
-const char * resolve_addr_text (const struct addrinfo *addr)
-{
-    static char text[1024];
-    char host[NI_MAXHOST], service[NI_MAXSERV];
-    int ret;
-
-    // lookup the reverse nameinfo
-    if ((ret = getnameinfo(
-                addr->ai_addr, addr->ai_addrlen, 
-                host, sizeof(host), service, sizeof(service), 
-                NI_NUMERICHOST | NI_NUMERICSERV
-    ))) {
-        strcpy(host, "???");
-        strcpy(service, "???");
-    }
-
-    // format message
-    snprintf(text, sizeof(text), "[%s]:%s", host, service);
-
-    // return static pointer
-    return text;
-}
--- a/src/resolve.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#ifndef RESOLVE_H
-#define RESOLVE_H
-
-/**
- * @file
- *
- * DNS resolver interface
- */
-#include "error.h"
-#include <netdb.h>
-
-/**
- * Lookup result state
- */
-struct resolve_result {
-    /** Head of the addrinfo list */
-    struct addrinfo *list;
-    
-    /** Current addrinfo item */
-    struct addrinfo *item;
-};
-
-/**
- * Resolve the given node/service tuple as a series of addrinfos for the given socktype.
- *
- * This will never return an empty result.
- *
- * XXX: blocking DNS stuff
- *
- * @param res where to store the result state
- * @param node hostname/address to look up
- * @param service service/port to look up
- * @param socktype a SOCK_* value to return addrinfo's for that socktype
- * @param ai_flags optional bitmask of AI_* flags to use
- * @param err returned error info
- */
-err_t resolve_addr (struct resolve_result *res, const char *node, const char *service, int socktype, int ai_flags, error_t *err);
-
-/**
- * Initialize the given result to zero
- */
-void resolve_result_init (struct resolve_result *res);
-
-/**
- * Get the next address from a result, if any left
- */
-struct addrinfo* resolve_result_next (struct resolve_result *res);
-
-/**
- * Release the addrinfo resources associated with the given result
- */
-void resolve_result_deinit (struct resolve_result *res);
-
-/**
- * Returns a pointer to a static buffer containing a string description of the given addrinfo
- */
-const char * resolve_addr_text (const struct addrinfo *addr);
-
-#endif /* RESOLVE_H */
--- a/src/service.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#include "service_internal.h"
-
-const struct object_type service_type_type = {
-    .parent = NULL,
-};
-
-void service_init (service_t *service, const struct service_type *type, const struct service_info *info)
-{
-    // init object
-    object_init(&service->base_obj, &type->base_type);
-
-    // store user info
-    service->info = *info;
-}
-
-void* service_check (service_t *service, const struct service_type *type)
-{
-    return object_cast(&service->base_obj, &type->base_type);
-}
-
-void service_error (service_t *service, const error_t *err)
-{
-    // just call the user callback
-    service->info.cb_tbl->on_error(service, err, service->info.cb_arg);
-}
-
-void service_destroy (service_t *service)
-{
-    const struct service_type *type = object_type(&service->base_obj, &service_type_type);
-
-    // invoke method
-    type->methods.deinit(service);
-
-    free(service); 
-}
--- a/src/service.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#ifndef SERVICE_H
-#define SERVICE_H
-
-/**
- * @file
- *
- * Defines a simple interface for creating services, which listen for connections and create transport_t's.
- */
-#include "transport.h"
-
-/**
- * Opaque state struct.
- */
-typedef struct service service_t;
-
-/**
- * User callbacks for services.
- */
-struct service_callbacks {
-    /**
-     * The service broke.
-     *
-     * This is only called for errors which occur when called directly from the event loop, and never for errors that
-     * occur inside of calls to service_*.
-     */
-    void (*on_error) (service_t *service, const error_t *err, void *arg);
-};
-
-/**
- * User info required to build a service
- */
-struct service_info {
-    /** Callback table */
-    const struct service_callbacks *cb_tbl;
-
-    /** Callback context arg */
-    void *cb_arg;
-
-    /** Settings for the service's client transports */
-    struct transport_info trans_info;
-};
-
-/**
- * Destroy a service to stop accepting any connections and release all resources.
- *
- * Any connected client transports should stay intact (?)
- */
-void service_destroy (service_t *service);
-
-#endif /* SERVICE_H */
--- a/src/service_internal.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-#ifndef SERVICE_INTERNAL_H
-#define SERVICE_INTERNAL_H
-
-/**
- * @file
- *
- * Internal interface for implementations of service_t
- */
-#include "service.h"
-#include "transport.h"
-#include "object.h"
-
-/**
- * The object_type of service_type
- */
-extern const struct object_type service_type_type;
-
-/**
- * Type definition with method table
- */
-struct service_type {
-    struct object_type base_type;
-    
-    /** Method table */
-    struct service_methods {
-        /**
-         * Release internal state, but not the service_t itself
-         */
-        void (*deinit) (service_t *service);
-    } methods;
-};
-
-/**
- * Base service_t state
- */
-struct service {
-    struct object base_obj;
-
-    /** User info */
-    struct service_info info;
-};
-
-/**
- * Initialize a service by binding it to a specific type, with the given user info for this service, and for spawned transports.
- */
-void service_init (service_t *service, const struct service_type *type, const struct service_info *info);
-
-/**
- * Used to up-cast a generic service_t pointer to an implementation of the given service_type (or subtype).
- *
- * It is a bug to call this with a service of a different type.
- */
-void* service_check (service_t *service, const struct service_type *type);
-
-/**
- * The service failed, call the user callback
- */
-void service_error (service_t *service, const error_t *err);
-
-#endif /* SERVICE_INTERNAL_H */
--- a/src/sock.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-
-#include "sock_internal.h"
-#include "ssl_internal.h"
-
-#include <assert.h>
-
-// global sock_stream_ctx instance
-struct sock_stream_ctx _sock_stream_ctx;
-
-err_t sock_init (struct event_base *ev_base, struct error_info *err)
-{
-    // store ev_base
-    _sock_stream_ctx.ev_base = ev_base;
-
-    // XXX: just call these all directly for now
-    if (ssl_global_init(err))
-        return ERROR_CODE(err);
-
-    // done
-    return SUCCESS;
-}
-
--- a/src/sock.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-#ifndef SOCK_H
-#define SOCK_H
-
-/**
- * @file
- *
- * Legacy sock_* interface for global state
- */
-#include "error.h"
-#include <sys/types.h>
-#include <event2/event.h>
-
-/**
- * Initialize the socket module's global state. Call this before calling any other sock_* functions.
- *
- * The given \a ev_base is the libevent base to use for nonblocking operation.
- *
- * @param ev_base the libevent base to use for events
- * @param err returned error info
- */
-err_t sock_init (struct event_base *ev_base, error_t *err);
-
-#endif
--- a/src/sock_internal.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#ifndef SOCK_INTERNAL_H
-#define SOCK_INTERNAL_H
-
-/**
- * @file
- *
- * internal sock_* interface
- */
-#include "sock.h"
-
-/**
- * Global sock_stream_ctx used for sock_init() and all sock_stream's
- */
-extern struct sock_stream_ctx {
-    /** libevent core */
-    struct event_base *ev_base;
-
-} _sock_stream_ctx;
-
-#endif /* SOCK_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_objs.c	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,462 @@
+#include "lua_objs.h"
+#include "lua_irc.h"
+#include "lua_func.h"
+#include "lua_thread.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Wrapper for module
+ */
+struct lua_module {
+    struct module *module;
+};
+
+static struct lua_type lua_module_type = LUA_TYPE("spbot.module");
+
+/**
+ * Create a lua_module userdata from the given module and push it onto the stack, returning 1.
+ *
+ * The given module should be a reference of its own right.
+ */
+static int lua_module_create (lua_State *L, struct module *module)
+{
+    // create the new obj
+    struct lua_module *lua_module = lua_type_create(L, &lua_module_type, sizeof(*lua_module));
+
+    // initialize
+    lua_module->module = module;
+
+    // ok
+    return 1;
+}
+
+/**
+ * module_put() our module reference
+ */
+static int lua_module__gc (lua_State *L)
+{
+    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
+    
+    // put it
+    module_put(lua_module->module);
+
+    return 0;
+}
+
+static int lua_module_conf (lua_State *L)
+{
+    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
+    const struct config_option *option;
+    struct error_info err;
+    bool is_err = true;
+
+    // the list of given config values, and temporary storage for string values
+    struct config_value values[CONFIG_VALUES_MAX], *value = values;
+    char *value_bufs[CONFIG_VALUES_MAX], **value_buf = value_bufs;
+
+    // number of arguments given
+    int nargs = lua_gettop(L), argidx = 2;
+
+    // init to zero
+    memset(values, 0, sizeof(values));
+    memset(value_bufs, 0, sizeof(value_bufs));
+
+    // XXX: come up with some better way...
+    struct nexus *nexus = lua_module->module->modules->nexus;
+
+    // the config name
+    const char *conf_name = luaL_checkstring(L, argidx++);
+
+    // look it up
+    if ((option = module_conf_lookup(lua_module->module, conf_name, &err)) == NULL)
+        return luaL_error(L, "module_conf_lookup: %s/%s: %s", module_name(lua_module->module), conf_name, error_msg(&err));
+
+    // maximum number of arguments accepted
+    int maxargs = config_params_count(option);
+
+    // too many arguments?
+    if (nargs - argidx > maxargs)
+        return luaL_error(L, "lua_module_conf: too many arguments (>%d) given (%d)", maxargs, nargs - argidx);
+    
+    // the current param
+    const struct config_param *param = option->params;
+
+    // apply each given argument to the correct param, storing it in value
+    for (; argidx <= nargs; argidx++, value++, param++) {
+        // the given config value
+        switch (lua_type(L, argidx)) {
+            case LUA_TNONE:
+            case LUA_TNIL:
+                // no value
+                value->type = CONFIG_NULL;
+
+                break;
+
+            case LUA_TSTRING: {
+                // string arg
+                const char *arg_str = lua_tostring(L, argidx);
+
+                // copy it as a mutable string buffer
+                if ((*value_buf = strdup(arg_str)) == NULL) {
+                    lua_pushfstring(L, "strdup");
+                    goto error;
+                }
+
+                // parse it as a raw value
+                if (config_parse_param(param, nexus, value, *value_buf, &err)) {
+                    lua_pushfstring(L, "config_parse: %s/%s: %s", option->name, *value_buf, error_msg(&err));
+                    goto error;
+                }
+
+                // seek to next value_buf
+                value_buf++;
+
+            } break;
+    
+            case LUA_TUSERDATA:
+                // some kind of userdata, use its metatable to figure out what type it is
+                if (!lua_getmetatable(L, argidx)) {
+                    lua_pushfstring(L, "config value is userdata without metatable");
+                    goto error;
+                }
+
+                // get the target metatable
+                lua_getfield(L, LUA_REGISTRYINDEX, "evirc.chan");
+
+                // is it a chan?
+                if (!lua_rawequal(L, -1, -2)) {
+                    lua_pushfstring(L, "config value is userdata of unknown type");
+                    goto error;
+                }
+
+                // pop the metatables
+                lua_pop(L, 2);
+
+                // get the irc_chan
+                struct lua_chan *lua_chan = lua_touserdata(L, argidx);
+
+                // build the value
+                value->type = CONFIG_IRC_CHAN;
+                value->irc_chan = lua_chan->chan;
+                
+                break;
+            
+            default:
+                lua_pushfstring(L, "config value is of unknown lua type '%s'", lua_typename(L, argidx));
+                goto error;
+
+        }
+    }
+
+    // apply it
+    if (module_conf(lua_module->module, option, values, &err)) {
+        lua_pushfstring(L, "module_conf: %s/%s: %s", module_name(lua_module->module), option->name, error_msg(&err));
+        goto error;
+    }
+    
+    // ok
+    is_err = false;
+
+error:
+    // release any allocated strings
+    for (value_buf = value_bufs; value_buf <= value_bufs + CONFIG_VALUES_MAX && *value_buf; value_buf++)
+        free(*value_buf);
+    
+    // either error or successful return
+    if (is_err)
+        return lua_error(L);
+    else
+        return 0;
+}
+
+static int lua_module_unload (lua_State *L)
+{
+    struct lua_module *lua_module = lua_type_get(L, &lua_module_type, 1);
+    struct error_info err;
+
+    // just unload it
+    if ((ERROR_CODE(&err) = module_unload(lua_module->module)))
+        return luaL_error(L, "module_unload: %s: %s", module_name(lua_module->module), error_msg(&err));
+
+    // ok
+    return 0;
+}
+
+static struct lua_method lua_module_methods[] = LUA_METHODS(
+        LUA_METHOD("__gc",      lua_module__gc,     NULL    ),
+        LUA_METHOD("conf",      lua_module_conf,    NULL    ),
+        LUA_METHOD("unload",    lua_module_unload,  NULL    )
+);
+
+/**
+ * Wrapper for modules
+ */
+struct lua_modules {
+    struct modules *modules;
+    
+    // strdup'd path for module_path
+    // XXX: remove when gc'd
+    char *path;
+};
+
+static struct lua_type lua_modules_type = LUA_TYPE("spbot.modules");
+
+static int lua_modules__gc (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
+    
+    // remove the modules path if it was set by us
+    if (lua_modules->path && modules_path(lua_modules->modules, NULL) == lua_modules->path)
+        modules_path(lua_modules->modules, "");
+
+    // release any strdup'd path
+    free(lua_modules->path);
+
+    // ok
+    return 0;
+}
+
+static int lua_modules_path (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
+    char *path = NULL;
+    const char *old_path;
+    
+    if (!lua_isnoneornil(L, 2)) {
+        // the new path
+        if ((path = strdup(luaL_checkstring(L, 2))) == NULL)
+            return luaL_error(L, "strdup");
+    }
+
+    // set or get
+    old_path = modules_path(lua_modules->modules, path);
+
+    // return the old path
+    if (old_path)
+        lua_pushstring(L, old_path);
+    else
+        lua_pushnil(L);
+
+    if (path) {
+        // replace the old path
+        free(lua_modules->path); 
+        lua_modules->path = path;
+    }
+
+    // ok
+    return 1;
+}
+
+static int lua_modules_load (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
+    struct module *module;
+    struct module_info info;
+    struct error_info err;
+
+    // the module name/path
+    info.name = luaL_checkstring(L, 2);
+    info.path = lua_isnoneornil(L, 3) ? NULL : luaL_checkstring(L, 3);
+
+    // load and get a new reference
+    if (module_load(lua_modules->modules, &module, &info, &err))
+        return luaL_error(L, "module_load: %s/%s: %s", info.name, info.path, error_msg(&err));
+
+    // wrap
+    return lua_module_create(L, module);
+}
+
+static int lua_modules_module (lua_State *L)
+{
+    struct lua_modules *lua_modules = lua_type_get(L, &lua_modules_type, 1);
+    struct module *module;
+    
+    // the module name
+    const char *name = luaL_checkstring(L, 2);
+
+    // look it up, as a new reference
+    if ((module = modules_get(lua_modules->modules, name)) == NULL)
+        return luaL_error(L, "module_get: %s: no such module", name);
+
+    // wrap
+    return lua_module_create(L, module);
+}
+
+static struct lua_method lua_modules_methods[] = LUA_METHODS(
+        LUA_METHOD("__gc",      lua_modules__gc,    NULL    ),
+        LUA_METHOD("path",      lua_modules_path,   NULL    ),
+        LUA_METHOD("load",      lua_modules_load,   NULL    ),
+        LUA_METHOD("module",    lua_modules_module, NULL    )
+    );
+
+
+
+/**
+ * Initialize the spbot.modules type for lua_modules, and registers an instance bound to the given modules list at
+ * 'modules'.
+ */
+static void lua_modules_init (lua_State *L, struct modules *modules)
+{
+    // allocate the global "modules" object
+    struct lua_modules *lua_modules = lua_type_register_global(L, &lua_modules_type, lua_modules_methods, "modules", sizeof(*lua_modules));
+    
+    // initialize it
+    lua_modules->modules = modules;
+}
+
+/**
+ * Wrapper for nexus
+ */
+struct lua_nexus {
+    struct nexus *nexus;
+};
+
+static struct lua_type lua_nexus_type = LUA_TYPE("spbot.nexus");
+
+static int lua_nexus_shutdown (lua_State *L)
+{
+    struct lua_nexus *lua_nexus = lua_type_get(L, &lua_nexus_type, 1);
+
+    // just shut it down
+    nexus_shutdown(lua_nexus->nexus);
+
+    return 0;
+}
+
+static int lua_nexus_load_config (lua_State *L)
+{
+    struct lua_nexus *lua_nexus = lua_type_get(L, &lua_nexus_type, 1);
+    struct error_info err;
+
+    const char *path = luaL_checkstring(L, 2);
+    
+    // just load it
+    if (nexus_load_config(lua_nexus->nexus, path, &err))
+        return luaL_error(L, "nexus_load_config(%s): %s", path, error_msg(&err));
+
+    return 0;
+}
+
+static struct lua_func lua_nexus_sleep_func = LUA_FUNC(&lua_nexus_type, "sleep",
+        "Schedules itself to resume after the given delay (in seconds) and yields",
+
+        LUA_FUNC_ARG_INT("tv_sec",  LUA_ARG_REQUIRED)
+    );
+
+static void lua_nexus_sleep_wakeup (evutil_socket_t fd, short what, void *arg)
+{
+    lua_State *L = arg;
+
+    (void) fd;
+    (void) what;
+
+    // resume the thread that called lua_nexus_sleep
+    lua_thread_resume_state(L);
+}
+
+static int lua_nexus_sleep (lua_State *L)
+{
+    struct lua_nexus *lua_nexus;
+    long tv_sec;
+
+    // parse args
+    lua_args_parse(L, &lua_nexus_sleep_func, (void *) &lua_nexus, &tv_sec);
+
+    // build tv
+    struct timeval tv = { tv_sec, 0 };
+    
+    // schedule wakeup
+    // use a pure-timeout event
+    if (event_base_once(lua_nexus->nexus->ev_base, -1, EV_TIMEOUT, lua_nexus_sleep_wakeup, L, &tv))
+        return luaL_error(L, "event_base_once");
+
+    // yield
+    return lua_thread_yield_state(L);
+}
+
+static struct lua_method lua_nexus_methods[] = LUA_METHODS(
+        LUA_METHOD("shutdown",      lua_nexus_shutdown,     NULL                    ),
+        LUA_METHOD("load_config",   lua_nexus_load_config,  NULL                    ),
+        LUA_METHOD("sleep",         lua_nexus_sleep,        &lua_nexus_sleep_func   )
+    );
+
+/**
+ * Initialize the spbot.nexus type for lua_nexus, and registers an instance bound to the given nexus list at
+ * 'nexus'.
+ */
+static void lua_nexus_init (lua_State *L, struct nexus *nexus)
+{
+    // allocate the global "nexus" object
+    struct lua_nexus *lua_nexus = lua_type_register_global(L, &lua_nexus_type, lua_nexus_methods, "nexus", sizeof(*lua_nexus));
+    
+    // initialize it
+    lua_nexus->nexus = nexus;
+}
+
+
+/**
+ * Global functions
+ */
+static int lua_log_level (lua_State *L)
+{
+    // log level as a string
+    enum log_level new_level = luaL_checkoption(L, 1, NULL, log_level_names);
+
+    // set it
+    set_log_level(new_level);
+
+    // ok
+    return 0;
+}
+
+static int lua_log (lua_State *L)
+{
+    // log level as a string
+    enum log_level level = luaL_checkoption(L, 1, NULL, log_level_names);
+    
+    // log message
+    const char *msg = luaL_checkstring(L, 2);
+
+    // log it
+    _log_msg(level, "lua", "%s", msg);
+
+    // ok
+    return 0;
+}
+
+static const struct luaL_Reg lua_global_functions[] = {
+    {   "log_level",    lua_log_level               },
+    {   "log",          lua_log                     },
+    {   NULL,           NULL                        }
+};
+
+static void lua_global_init (lua_State *L)
+{
+    const struct luaL_Reg *reg;
+
+    for (reg = lua_global_functions; reg->name && reg->func; reg++) {
+        // put the function on the stack
+        lua_pushcfunction(L, reg->func);
+
+        // set the global
+        lua_setglobal(L, reg->name);
+    }
+}
+
+void lua_objs_init (struct nexus_lua *lua)
+{
+    // register types
+    lua_type_register(lua->st, &lua_module_type, lua_module_methods);
+    
+    // globals
+    lua_nexus_init(lua->st, lua->nexus);
+    lua_modules_init(lua->st, lua->nexus->modules);
+    
+    // global functions
+    lua_global_init(lua->st);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_objs.h	Thu May 28 01:17:36 2009 +0300
@@ -0,0 +1,21 @@
+#ifndef LUA_OBJS_H
+#define LUA_OBJS_H
+
+/**
+ * @file
+ *
+ * Defines lua functions to access the various objects in a nexus
+ */
+#include "nexus_lua.h"
+
+#include <lua5.1/lua.h>
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Registers our lua runtime objects into the given lua state.
+ *
+ * Call in protected mode.
+ */
+void lua_objs_init (struct nexus_lua *lua);
+
+#endif /* LUA_OBJS_H */
--- a/src/spbot/nexus.c	Thu May 28 00:35:02 2009 +0300
+++ b/src/spbot/nexus.c	Thu May 28 01:17:36 2009 +0300
@@ -1,6 +1,6 @@
 #include "nexus.h"
 #include "lua_config.h"
-#include "sock.h"
+#include <lib/sock.h>
 #include <lib/log.h>
 
 #include <stdlib.h>
--- a/src/spbot/nexus_lua.c	Thu May 28 00:35:02 2009 +0300
+++ b/src/spbot/nexus_lua.c	Thu May 28 01:17:36 2009 +0300
@@ -1,5 +1,5 @@
 #include "nexus_lua.h"
-#include "../lua_objs.h"
+#include "lua_objs.h"
 #include "../lua_irc.h"
 
 #include <stdlib.h>
@@ -37,14 +37,14 @@
 
     // alloc
     if ((lua = calloc(1, sizeof(*lua))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
+        return SET_ERROR_MEM(err);
 
     // store
     lua->nexus = nexus;
 
     // create the lua state
     if ((lua->st = luaL_newstate()) == NULL)
-        JUMP_SET_ERROR(err, ERR_LUA_MEM);
+        JUMP_SET_ERROR(err, &lua_errors, ERR_LUA_MEM);
     
     // init in protected mode
     if (nexus_lua_error(lua->st, lua_cpcall(lua->st, &nexus_lua_init, lua), err))
@@ -74,7 +74,7 @@
     int ret;
     bool loaded = false;
 
-    RESET_ERROR(err);
+    error_reset(err);
 
     // load the line as a lua function
     if ((ret = luaL_loadstring(lua->st, chunk)))
@@ -105,13 +105,13 @@
     const char *error = lua_tostring(L, -1);
 
     switch (ret) {
-        case 0:                 RETURN_SET_ERROR(err, SUCCESS);
-        case LUA_ERRSYNTAX:     RETURN_SET_ERROR_STR(err, ERR_LUA_SYNTAX, error);
-        case LUA_ERRRUN:        RETURN_SET_ERROR_STR(err, ERR_LUA_RUN, error);
-        case LUA_ERRMEM:        RETURN_SET_ERROR_STR(err, ERR_LUA_MEM, error);
-        case LUA_ERRERR:        RETURN_SET_ERROR_STR(err, ERR_LUA_ERR, error);
-        case LUA_ERRFILE:       RETURN_SET_ERROR_STR(err, ERR_LUA_FILE, error);
-        default:                RETURN_SET_ERROR_EXTRA(err, ERR_UNKNOWN, ret);
+        case 0:                 return SUCCESS;
+        case LUA_ERRSYNTAX:     return SET_ERROR_STR(err, &lua_errors, ERR_LUA_SYNTAX, error);
+        case LUA_ERRRUN:        return SET_ERROR_STR(err, &lua_errors, ERR_LUA_RUN, error);
+        case LUA_ERRMEM:        return SET_ERROR_STR(err, &lua_errors, ERR_LUA_MEM, error);
+        case LUA_ERRERR:        return SET_ERROR_STR(err, &lua_errors, ERR_LUA_ERR, error);
+        case LUA_ERRFILE:       return SET_ERROR_STR(err, &lua_errors, ERR_LUA_FILE, error);
+        default:                return SET_ERROR(err, &general_errors, ERR_UNKNOWN);
     };
 }
 
--- a/src/ssl.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-#include "ssl_internal.h"
-
-#include <assert.h>
-
-/*
- * Global shared anonymous client credentials
- */
-struct ssl_client_cred ssl_client_cred_anon = { .x509 = NULL, .verify = false, .refcount = 0 };
-
-
-// XXX: GnuTLS log func
-void _log (int level, const char *msg)
-{
-    printf("gnutls: %d: %s", level, msg);
-}
-
-err_t ssl_global_init (error_t *err)
-{
-    // global init
-    if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
-        return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
-
-    // initialize the anon client credentials
-    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&ssl_client_cred_anon.x509)) < 0)
-        return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-
-    // XXX: debug
-//    gnutls_global_set_log_function(&_log);
-//    gnutls_global_set_log_level(11);
-
-    // done
-    return SUCCESS;
-}
-
-static void ssl_client_cred_destroy (struct ssl_client_cred *cred)
-{
-    // simple
-    gnutls_certificate_free_credentials(cred->x509);
-
-    free(cred);
-}
-
-err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
-        const char *cafile_path, bool verify,
-        const char *cert_path, const char *pkey_path,
-        error_t *err
-) {
-    struct ssl_client_cred *cred;
-
-    // alloc it
-    if ((cred = calloc(1, sizeof(*cred))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // create the cert
-    if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&cred->x509)) < 0)
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-    
-    // load the trusted ca certs?
-    if (cafile_path) {
-        // load them
-        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_trust_file(cred->x509, cafile_path, GNUTLS_X509_FMT_PEM)) < 0)
-            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_TRUST_FILE);
-
-    }
-
-    // set the verify flags?
-    cred->verify = verify;
-    gnutls_certificate_set_verify_flags(cred->x509, 0);
-
-    // load the client cert?
-    if (cert_path || pkey_path) {
-        // need both...
-        assert(cert_path && pkey_path);
-
-        // load
-        if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_key_file(cred->x509, cert_path, pkey_path, GNUTLS_X509_FMT_PEM)))
-            JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_KEY_FILE);
-    }
-
-    // ok
-    cred->refcount = 1;
-    *ctx_cred = cred;
-
-    return SUCCESS;
-
-error:
-    // release
-    ssl_client_cred_destroy(cred);
-
-    return ERROR_CODE(err);
-}
-
-void ssl_client_cred_get (struct ssl_client_cred *cred)
-{
-    cred->refcount++;
-}
-
-void ssl_client_cred_put (struct ssl_client_cred *cred)
-{
-    if (--cred->refcount == 0)
-        ssl_client_cred_destroy(cred);
-}
--- a/src/ssl.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-#ifndef SSL_H
-#define SSL_H
-
-/**
- * @file
- *
- * SSL transport implementation.
- */
-#include "transport.h"
-#include <stdbool.h>
-
-/**
- * SSL client credentials for use with ssl_client_credentials/sock_ssl_connect
- */
-struct ssl_client_cred;
-
-/**
- * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
- * ourselves to the server, as well as to verify the server.
- *
- * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
- * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
- * warn. XXX: not entirely true
- *
- * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
- * anonymous client certificate will be used. Both must be supplied if given.
- *
- * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
- *
- * @param ctx_cred the newly created client credentials are returned via this
- * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
- * @param verify force verification of the peer cert
- * @param cert_path path to the client certificate file, or NULL
- * @param pkey_path path to the client private key, or NULL
- * @param err returned error info
- */
-err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
-        const char *cafile_path, bool verify,
-        const char *cert_path, const char *pkey_path,
-        error_t *err
-);
-
-/**
- * Aquire a referenec for the given cred.
- */
-void ssl_client_cred_get (struct ssl_client_cred *cred);
-
-/**
- * Release a reference allocated for the given cred.
- */
-void ssl_client_cred_put (struct ssl_client_cred *cred);
-
-/**
- * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
- * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
- * the given \a cb_func.
- *
- * The given ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
- * or a ssl_client_cred allocated using ssl_client_cred_create(). The contexts are reference-counted, so once
- * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
- * 
- * @param info the transport setup info
- * @param transport_ptr returned transport
- * @param hostname the hostname to connect to
- * @param service the TCP service name (i.e. port) to connect to
- * @param cred the SSL client credentials to use, if not NULL
- * @param err returned error info
- */
-err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *hostname, const char *service,
-        struct ssl_client_cred *cred,
-        error_t *err
-    );
-
-
-
-#endif /* SSL_H */
--- a/src/ssl_client.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,454 +0,0 @@
-#include "ssl_internal.h"
-
-#include <gnutls/x509.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-// XXX: remove
-#include "log.h"
-#include <assert.h>
-
-
-/**
- * Cast a ssl_client to a sock_fd.
- */
-#define SSL_CLIENT_FD(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd)
-
-/**
- * Cast a ssl_client to a sock_stream.
- */
-#define SSL_CLIENT_TRANSPORT(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd.base)
-
-
-
-/**
- * Enable the TCP events based on the session's gnutls_record_get_direction().
- */
-static err_t ssl_client_ev_enable (struct ssl_client *client, error_t *err)
-{
-    int ret;
-    short mask;
-
-    // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
-    // operation, so we can use that to determine what events to register
-    switch ((ret = gnutls_record_get_direction(client->session))) {
-        case 0: 
-            // read more data
-            mask = TRANSPORT_READ;
-            break;
-        
-        case 1:
-            // write buffer full
-            mask = TRANSPORT_WRITE;
-            break;
-        
-        default:
-            // random error
-            RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
-    }
-    
-    // do the enabling
-    if ((ERROR_CODE(err) = transport_fd_enable(SSL_CLIENT_FD(client), mask)))
-        return ERROR_CODE(err);
-    
-
-    return SUCCESS;
-}
-
-/**
- * Translate a set of gnutls_certificate_status_t values to a constant error message
- */
-static const char* ssl_client_verify_error (unsigned int status)
-{
-    if (status & GNUTLS_CERT_REVOKED)
-        return "certificate was revoked";
-
-    else if (status & GNUTLS_CERT_INVALID) {
-        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-            return "certificate signer was not found";
-
-        else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
-            return "certificate signer is not a Certificate Authority";
-
-        else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
-            return "certificate signed using an insecure algorithm";
-
-        else
-            return "certificate could not be verified";
-
-    } else
-        return "unknown error";
-
-}
-
-/**
- * Perform the certificate validation procedure on the peer cert.
- *
- * Based on the GnuTLS examples/ex-rfc2818.c
- */
-static err_t ssl_client_verify (struct ssl_client *client, error_t *err)
-{
-    unsigned int status;
-    const gnutls_datum_t *cert_list;
-    unsigned int cert_list_size;
-    gnutls_x509_crt_t cert = NULL;
-    time_t t, now;
-
-    // init
-    RESET_ERROR(err);
-    now = time(NULL);
-    
-    // inspect the peer's cert chain using the installed trusted CAs
-    if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(client->session, &status)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
-
-    // verify errors?
-    if (status)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, ssl_client_verify_error(status));
-    
-    // import the main cert
-    assert(gnutls_certificate_type_get(client->session) == GNUTLS_CRT_X509);
-
-    if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
-
-    if ((cert_list = gnutls_certificate_get_peers(client->session, &cert_list_size)) == NULL)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
-
-    if (!cert_list_size)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
-
-    if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
-    
-    // check expire/activate... not sure if we need to do this
-    if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
-
-    if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
-    
-    // check hostname
-    if (!gnutls_x509_crt_check_hostname(cert, client->hostname))
-        JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
-
-error:
-    // cleanup
-    if (cert)
-        gnutls_x509_crt_deinit(cert);
-    
-    // should be SUCCESS
-    return ERROR_CODE(err);    
-}
-
-
-/**
- * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
- *
- * This updates the ssl_client::handshake state internally, as used by ssl_client_event_handler.
- *
- * If the client is marked as verify, this will perform the verification, returning on any errors, and then unset the
- * verify flag - this ensures that the peer cert is only verified once per connection...
- *
- * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
- */
-static int ssl_client_handshake (struct ssl_client *client, error_t *err)
-{
-    int ret;
-
-    // perform the handshake
-    if ((ret = gnutls_handshake(client->session)) < 0 && ret != GNUTLS_E_AGAIN)
-        JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
-    
-    // complete?
-    if (ret == 0) {
-        // update state
-        client->handshake = false;
-
-        // verify?
-        if (client->verify) {
-            // perform the validation
-            if (ssl_client_verify(client, err))
-                goto error;
-            
-            // unmark
-            client->verify = false;
-        }
-
-        // handshake done
-        return 1;
-
-    } else {
-        // set state, isn't really needed every time, but easier this way
-        client->handshake = true;
-
-        // re-enable the event for the next iteration
-        return ssl_client_ev_enable(client, err);
-    }
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-/**
- * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
- */
-static void ssl_client_on_event (struct transport_fd *fd, short what, void *arg)
-{
-    struct ssl_client *client = arg;
-    error_t err;
-
-    (void) fd;
-
-    // XXX: timeouts
-    (void) what;
-
-    // are we in the handshake cycle?
-    if (client->handshake) {
-        RESET_ERROR(&err);
-
-        // perform the next handshake step
-        // this returns zero when the handshake is not yet done, errors/completion then trigger the else-if-else below
-        if (ssl_client_handshake(client, &err) == 0) {
-            // handshake continues
-        
-        } else if (!SSL_CLIENT_TRANSPORT(client)->connected) {
-            // the async connect+handshake process has completed
-            // invoke the user connect callback directly with appropriate error
-            transport_connected(SSL_CLIENT_TRANSPORT(client), ERROR_CODE(&err) ? &err : NULL, true);
-
-        } else {
-            // in-connection re-handshake completed
-            if (ERROR_CODE(&err))
-                // the re-handshake failed, so this transport is dead
-                transport_error(SSL_CLIENT_TRANSPORT(client), &err);
-        
-            else
-                // re-handshake completed, so continue with the transport_callbacks
-                transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
-        }
-
-    } else {
-        // normal transport operation
-        // gnutls might be able to proceed now, so invoke user callbacks
-        transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
-    }
-}
-
-static err_t ssl_client__read (transport_t *transport, void *buf, size_t *len, error_t *err)
-{
-    struct ssl_client *client = transport_check(transport, &ssl_client_type);
-    int ret;
-    
-    // read gnutls record
-    do {
-        ret = gnutls_record_recv(client->session, buf, *len);
-
-    } while (ret == GNUTLS_E_INTERRUPTED);
-    
-    // errors
-    // XXX: E_REHANDSHAKE?
-    if (ret < 0 && ret != GNUTLS_E_AGAIN)
-        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
-    
-    else if (ret == 0)
-        return SET_ERROR(err, ERR_EOF);
-
-
-    // EAGAIN?
-    if (ret < 0) {
-        *len = 0;
-
-    } else {
-        // updated length
-        *len = ret;
-
-    }
-
-    return SUCCESS;
-}
-
-static err_t ssl_client__write (transport_t *transport, const void *buf, size_t *len, error_t *err)
-{
-    struct ssl_client *client = transport_check(transport, &ssl_client_type);
-    int ret;
- 
-    // read gnutls record
-    do {
-        ret = gnutls_record_send(client->session, buf, *len);
-   
-    } while (ret == GNUTLS_E_INTERRUPTED);
-
-    // errors
-    if (ret < 0 && ret != GNUTLS_E_AGAIN)
-        RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_SEND, ret);
-    
-    else if (ret == 0)
-        return SET_ERROR(err, ERR_WRITE_EOF);
-
-
-    // eagain?
-    if (ret < 0) {
-        *len = 0;
-
-    } else {
-        // updated length
-        *len = ret;
-    }
-
-    return SUCCESS;
-}
-
-void ssl_client_deinit (struct ssl_client *client)
-{
-    // close the session rudely
-    gnutls_deinit(client->session);
-    client->session = NULL;
- 
-    // terminate the TCP transport
-    tcp_client_deinit(&client->base_tcp);
-   
-    if (client->cred) {
-        // drop the cred ref
-        ssl_client_cred_put(client->cred);
-
-        client->cred = NULL;
-    }
-
-    // free
-    free(client->hostname);
-    client->hostname = NULL;
-}
-
-
-static void ssl_client__deinit (transport_t *transport)
-{
-    struct ssl_client *client = transport_check(transport, &ssl_client_type);
-    
-    // die
-    ssl_client_deinit(client);
-}
-
-/**
- * Our tcp_client-invoked connect handler
- */
-static void ssl_client__connected (transport_t *transport, const error_t *tcp_err)
-{
-    struct ssl_client *client = transport_check(transport, &ssl_client_type);
-    error_t err;
-
-    // trap errors to let the user handle them directly
-    if (tcp_err)
-        JUMP_SET_ERROR_INFO(&err, tcp_err);
-    
-    // bind default transport functions (recv/send) to use the TCP fd
-    gnutls_transport_set_ptr(client->session, (gnutls_transport_ptr_t) (long int) SSL_CLIENT_FD(client)->fd);
-
-    // add ourselves as the event handler
-    if ((ERROR_CODE(&err) = transport_fd_setup(SSL_CLIENT_FD(client), ssl_client_on_event, client)))
-        goto error;
-
-    // start handshake
-    if (ssl_client_handshake(client, &err))
-        // this should complete with SUCCESS if it returns >0
-        goto error;
-
-    // ok, so we wait...
-    return;
-
-error:
-    // tell the user
-    transport_connected(transport, &err, true);
-}
-
-struct transport_type ssl_client_type = {
-    .base_type = {
-        .parent     = &tcp_client_type.base_type,
-    },
-    .methods = {
-        .read       = ssl_client__read,
-        .write      = ssl_client__write,
-        .deinit     = ssl_client__deinit,
-        ._connected = ssl_client__connected,
-    },
-};
-
-
-
-static void ssl_client_destroy (struct ssl_client *client)
-{
-    ssl_client_deinit(client);
-
-    free(client);
-}
-
-err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *hostname, const char *service,
-        struct ssl_client_cred *cred,
-        error_t *err
-    )
-{
-    struct ssl_client *client = NULL;
-
-    // alloc
-    if ((client = calloc(1, sizeof(*client))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // initialize base
-    transport_init(SSL_CLIENT_TRANSPORT(client), &ssl_client_type, info);
-
-    if (!cred) {
-        // default credentials
-        cred = &ssl_client_cred_anon;
-    
-    } else {
-        // take a ref
-        client->cred = cred;
-        cred->refcount++;
-    };
-
-    // do verify?
-    if (cred->verify)
-        client->verify = true;
-
-    // init
-    if ((client->hostname = strdup(hostname)) == NULL)
-        JUMP_SET_ERROR(err, ERR_STRDUP);
-
-    // initialize TCP
-    tcp_client_init(&client->base_tcp);
-
-    // initialize client session
-    if ((ERROR_EXTRA(err) = gnutls_init(&client->session, GNUTLS_CLIENT)) < 0)
-        JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
-
-    // ...default priority stuff
-    if ((ERROR_EXTRA(err) = gnutls_set_default_priority(client->session)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
-
-    // XXX: silly hack for OpenSSL interop
-    gnutls_dh_set_prime_bits(client->session, 512);
-
-    // bind credentials
-    if ((ERROR_EXTRA(err) = gnutls_credentials_set(client->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
-        JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
-
-    // TCP connect
-    if (tcp_client_connect_async(&client->base_tcp, hostname, service, err))
-        goto error;
-
-    // done, wait for the connect to complete
-    *transport_ptr = SSL_CLIENT_TRANSPORT(client);
-
-    return SUCCESS;
-
-error:
-    // cleanup
-    ssl_client_destroy(client);
-
-    return ERROR_CODE(err);    
-}
-
-
--- a/src/ssl_internal.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-#ifndef SSL_INTERNAL_H
-#define SSL_INTERNAL_H
-
-/**
- * @file
- *
- * A sock_stream implementation using GnuTLS for SSL
- */
-#include "ssl.h"
-#include "tcp_internal.h"
-
-#include <gnutls/gnutls.h>
-
-/**
- * GnuTLS library error codes
- */
-enum ssl_error_code {
-    _ERR_SSL_BEGIN = _ERR_GNUTLS,
-    
-    ERR_GNUTLS_CERT_ALLOC_CRED,
-    ERR_GNUTLS_GLOBAL_INIT,
-    ERR_GNUTLS_INIT,
-    ERR_GNUTLS_SET_DEFAULT_PRIORITY,
-    ERR_GNUTLS_CRED_SET,
-    ERR_GNUTLS_HANDSHAKE,
-    ERR_GNUTLS_RECORD_SEND,
-    ERR_GNUTLS_RECORD_RECV,
-    ERR_GNUTLS_RECORD_GET_DIRECTION,   
-    ERR_GNUTLS_CERT_VERIFY_PEERS2,
-    ERR_GNUTLS_CERT_VERIFY,
-    ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
-    ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
-};
-
-/**
- * GnuTLS credentials for client sockets.
- */
-struct ssl_client_cred {
-    /** Our client certificate */
-    gnutls_certificate_credentials_t x509;
-
-    /** Should we verify? */
-    bool verify;
-
-    /** Refcount from ssl_client */
-    int refcount;
-};
-
-/**
- * Global anonymous x509 credentials
- */
-extern struct ssl_client_cred ssl_client_cred_anon;
-
-/*
- * Our transport_type
- */
-extern struct transport_type ssl_client_type;
-
-/**
- * An SSL-encrypted TCP connection, using libgnutls
- */
-struct ssl_client {
-    /** The underlying TCP connection */
-    struct tcp_client base_tcp;
-
-    /** The hostname we connected to, for verification */
-    char *hostname;
-
-    /** The credentials we are using, unless anon */
-    struct ssl_client_cred *cred;
-    
-    /** The GnuTLS session for this connection */
-    gnutls_session_t session;
-
-    /** Should we verify the peer cert? */
-    bool verify;
-
-    /** Are we running a handshake? */
-    bool handshake;
-};
-
-/**
- * Initialize the global gnutls state
- */
-err_t ssl_global_init (error_t *err);
-
-#endif /* SSL_INTERNAL_H */
--- a/src/tcp.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#include "tcp_internal.h"
-
-int tcp_sock_create (const struct addrinfo *addr, error_t *err)
-{
-    int sock;
-
-    // create a new socket using addr->ai_family/socktype/protocol
-    if ((sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
-        JUMP_SET_ERROR_ERRNO(err, ERR_SOCKET);
-
-    return sock;
-
-error:
-    return -ERROR_CODE(err);    
-}
-
-err_t tcp_sock_error (evutil_socket_t sock, error_t *err)
-{
-    int optval;
-    socklen_t optlen;
-
-    RESET_ERROR(err);
-
-    // init params
-    optval = 0;
-    optlen = sizeof(optval);
-
-    // read error code
-    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen))
-        RETURN_SET_ERROR_ERRNO(err, ERR_GETSOCKOPT);
-    
-    // sanity-check optlen... not sure if this is sensible
-    if (optlen != sizeof(optval))
-        RETURN_SET_ERROR_EXTRA(err, ERR_GETSOCKOPT, EINVAL);
-    
-    // then store the system error code
-    ERROR_EXTRA(err) = optval;
-
-    // ok
-    return SUCCESS;
-}
-
-
--- a/src/tcp.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#ifndef TCP_H
-#define TCP_H
-
-/**
- * @file
- *
- * TCP transport/service implementation.
- *
- * XXX: provide some TCP-specific type/functions?
- */
-#include "transport.h"
-#include "service.h"
-
-/**
- * Connect the given transport via TCP to the given host/service. The transport will not be ready for use until the
- * transport_callbacks::on_connect callback has been invoked.
- *
- * XXX: blocking DNS resolution
- *
- * @param info the transport setup info
- * @param transport_ptr returned transport
- * @param host the hostname to connect to
- * @param service the service name (i.e. port) to connect to
- * @param err returned error info
- */
-err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *host, const char *service, error_t *err);
-
-/**
- * Create a passive/listening TCP socket on the given interface/port (NULL to pick automatically).
- */
-err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
-        const char *interface, const char *service, error_t *err);
-
-#endif
--- a/src/tcp_client.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,278 +0,0 @@
-#include "tcp_internal.h"
-#include "log.h"
-
-/*
- * Our transport methods
- */
-static void tcp_client__deinit (transport_t *transport)
-{
-    struct tcp_client *client = transport_check(transport, &tcp_client_type);
-    
-    // proxy
-    tcp_client_deinit(client);
-}
-
-/*
- * Our transport_type
- */
-const struct transport_type tcp_client_type = {
-    .base_type = {
-        .parent             = &tcp_transport_type.base_type,
-    },
-    .methods                = {
-        .read               = transport_fd__read,
-        .write              = transport_fd__write,
-        .events             = transport_fd__events,
-        .deinit             = tcp_client__deinit,
-    },
-};
-
-/*
- * Forward-declare 
- */
-static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg);
-
-/*
- * Function implementations
- */
-void tcp_client_init (struct tcp_client *client)
-{
-    tcp_transport_init(&client->base_trans, -1);
-    
-    resolve_result_init(&client->rr);
-}
-
-/*
- * Start connecting to the given address in a non-blocking fashion. Returns any errors that immediately crop up,
- * otherwise eventually calls tcp_client_connect_done().
- */
-static err_t tcp_client_connect_addr (struct tcp_client *client, struct addrinfo *addr, error_t *err)
-{
-    struct transport_fd *_fd = &client->base_trans.base_fd;
-    int ret;
-    evutil_socket_t sock;
-    err_t tmp;
-
-    // first, create the socket
-    if ((sock = tcp_sock_create(addr, err)) < 0)
-        return ERROR_CODE(err);
-
-    // set it as our sock
-    if ((ERROR_CODE(err) = transport_fd_set(_fd, sock)))
-        goto error;
-
-    // then, set it up as nonblocking
-    if ((ERROR_CODE(err) = transport_fd_nonblock(_fd, true)))
-        goto error;
-
-    // then, initiate the connect
-    if ((ret = connect(sock, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS) 
-        JUMP_SET_ERROR_ERRNO(err, ERR_CONNECT);
-    
-    if (ret < 0) {
-        // ok, connect started, setup our completion callback
-        if ((ERROR_CODE(err) = transport_fd_setup(_fd, tcp_client_on_connect, client)))
-            goto error;
-    
-        // enable for write
-        if ((ERROR_CODE(err) = transport_fd_enable(_fd, TRANSPORT_WRITE)))
-            goto error;
-
-    } else {
-        // oops... blocking connect - fail to avoid confusion
-        // XXX: come up with a better error name to use
-        // XXX: support non-async connects as well
-        JUMP_SET_ERROR_EXTRA(err, ERR_CONNECT, EINPROGRESS);
-    }
-    
-    // ok
-    return SUCCESS;
-
-error:
-    // close the stuff we did open
-    if ((tmp = transport_fd_close(_fd)))
-        log_warn("error closing socket after connect error: %s", error_name(tmp));
-
-    return ERROR_CODE(err);
-}
-
-
-/*
- * Attempt to connect to the next addrinfo, or the next one, if that fails, etc.
- *
- * This does not call transport_connected().
- */
-static err_t tcp_client_connect_continue (struct tcp_client *client, error_t *err)
-{
-    struct addrinfo *addr;
-
-    // try and connect to each one until we find one that works
-    while ((addr = resolve_result_next(&client->rr))) {
-        // attempt to start connect
-        if (tcp_client_connect_addr(client, addr, err) == SUCCESS)
-            break;
-
-        // log a warning on the failed connect
-        log_warn_error(err, "%s", resolve_addr_text(addr));
-    }
-    
-
-    if (addr)
-        // we succesfully did a tcp_client_connect_addr on valid address
-        return SUCCESS;
-
-    else
-        // all of the connect_async_addr's failed, return the last error
-        return ERROR_CODE(err);
-}
-
-/*
- * Cleanup our resolver state and any connect callbacks after a connect
- */
-static void tcp_client_connect_cleanup (struct tcp_client *client)
-{
-    // drop the resolver stuff
-    resolve_result_deinit(&client->rr);
-    
-    // remove our event handler
-    transport_fd_clear(&client->base_trans.base_fd);
-}
-
-/*
- * Our async connect operation has completed, clean up, set up state for event-based operation with user callbacks, and
- * invoke transport_connected().
- *
- * The given \a err should be NULL for successful completion, or the error for failures.
- */
-static void tcp_client_connect_done (struct tcp_client *client, error_t *conn_err)
-{
-    error_t err;
-
-    // cleanup
-    tcp_client_connect_cleanup(client);
-
-    if (conn_err)
-        JUMP_SET_ERROR_INFO(&err, conn_err);
-
-    // let the transport handle the rest
-    if (tcp_transport_connected(&client->base_trans, &err))
-        goto error;
-    
-    // ok
-    return;
-
-error:    
-    // pass the error on to transport
-    transport_connected(&client->base_trans.base_fd.base, &err, false);
-}
-
-/*
- * Our async connect callback
- */
-static void tcp_client_on_connect (struct transport_fd *fd, short what, void *arg)
-{
-    struct tcp_client *client = arg;
-    error_t err;
-    err_t tmp;
-
-    // XXX: timeouts
-    (void) what;
-    
-    // read socket error code
-    if (tcp_sock_error(client->base_trans.base_fd.fd, &err))
-        goto error;
-
-    // did the connect fail?
-    if (ERROR_EXTRA(&err))
-        JUMP_SET_ERROR(&err, ERR_CONNECT);
-    
-    // done, success
-    return tcp_client_connect_done(client, NULL);
-
-error:
-    // close the socket
-    if ((tmp = transport_fd_close(fd)))
-        log_warn("error closing socket after connect error: %s", error_name(tmp));
-
-    // log a warning
-    log_warn_error(&err, "connect to %s failed", "???");
-
-    // try the next one or fail completely
-    if (tcp_client_connect_continue(client, &err))
-        tcp_client_connect_done(client, &err);
-}
-
-err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err)
-{
-    // do the resolving
-    if (resolve_addr(&client->rr, hostname, service, SOCK_STREAM, 0, err))
-        return ERROR_CODE(err);
-
-    // start connecting with the first result
-    if (tcp_client_connect_continue(client, err))
-        goto error;
-
-    // ok
-    return SUCCESS;
-
-error:
-    // cleanup
-    resolve_result_deinit(&client->rr);
-    
-    return ERROR_CODE(err);
-}
-
-void tcp_client_deinit (struct tcp_client *client)
-{
-    // cleanup our stuff
-    resolve_result_deinit(&client->rr);
-    
-    // deinit lower transport
-    tcp_transport_deinit(&client->base_trans);
-}
-
-/*
- * Deinit and free, not using the transport interface
- */
-static void tcp_client_destroy (struct tcp_client *client)
-{
-    tcp_client_deinit(client);
-
-    free(client);
-}
-
-/*
- * Public interface
- */
-err_t tcp_connect (const struct transport_info *info, transport_t **transport_ptr, 
-        const char *host, const char *service, error_t *err)
-{
-    struct tcp_client *client;
- 
-    // alloc
-    if ((client = calloc(1, sizeof(*client))) == NULL)
-        return ERR_CALLOC;
-
-    // init transport
-    transport_init(&client->base_trans.base_fd.base, &tcp_client_type, info);
-    
-    // init our state
-    tcp_client_init(client);
- 
-    // begin connect
-    if (tcp_client_connect_async(client, host, service, err))
-        goto error;
-
-    // good
-    *transport_ptr = &client->base_trans.base_fd.base;
-
-    return 0;
-
-error:
-    // cleanup
-    tcp_client_destroy(client);
-        
-    // return error code
-    return ERROR_CODE(err);
-}
-
--- a/src/tcp_internal.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-#ifndef TCP_INTERNAL_H
-#define TCP_INTERNAL_H
-
-/**
- * @file
- *
- * Internal TCP interface for implementations
- */
-#include "tcp.h"
-#include "resolve.h"
-#include "transport_fd.h"
-#include "transport_internal.h"
-#include "service_internal.h"
-#include "error.h"
-
-#include <event2/event.h>
-#include <event2/util.h>
-
-/**
- * Create a new socket() using the given addr's family/socktype/protocol and return it.
- *
- * In case of errors, this returns -err_t
- *
- * @param addr the addrinfo to create the socket for
- * @param err returned error info
- * @return new fd on success, -err_t on error
- */
-int tcp_sock_create (const struct addrinfo *addr, error_t *err);
-
-/**
- * Return the socket's current error code via err->extra.
- *
- * In case getting the socket error code itself fails, this will return normal error code/info.
- *
- * Otherwise, this will return SUCCESS, with the errno value stored in err->extra.
- */
-err_t tcp_sock_error (evutil_socket_t sock, error_t *err);
-
-
-/**
- * TCP transport type
- */
-extern const struct transport_type tcp_transport_type;
-
-/**
- * Base TCP transport 
- *
- * XXX: currently just the same as transport_fd, but this will probably change
- */
-struct tcp_transport {
-    /** Base FD-based implementation */
-    struct transport_fd base_fd;
-};
-
-/**
- * Initialize the tcp_transport state.
- *
- * This initializes the transport_fd base using the global sock_ctx::ev_base and the given socket.
- */
-void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock);
-
-/**
- * Create a new tcp_transport with the given sock.
- *
- * For convenience, this will also make the sock nonblocking.
- *
- * In case of errors, this will the socket.
- *
- * @param trans_ptr returned tcp_transport
- * @param info the transport user settings
- * @param sock the unused TCP socket
- * @param err returned error info
- */
-err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err);
-
-/**
- * The transport as now connected, this sets up the intitial user settings, and invokes the callback.
- *
- * XXX: this does an 'indirect' call to transport_connected().
- *
- * @param err returned error info
- */
-err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err);
-
-/**
- * Deinitialize the transport state, terminating the connection and releasing resources.
- */
-void tcp_transport_deinit (struct tcp_transport *trans);
-
-/**
- * Deinitialize and free the given tcp_transport
- */
-void tcp_transport_destroy (struct tcp_transport *trans);
-
-/**
- * TCP client transport type
- */
-extern const struct transport_type tcp_client_type;
-
-/**
- * TCP client state
- */
-struct tcp_client {
-    /** Base transport stuff */
-    struct tcp_transport base_trans;
-
-    /** The resolver lookup result for the async connect process */
-    struct resolve_result rr;
-};
-
-/**
- * Initialize the tcp_client state
- */
-void tcp_client_init (struct tcp_client *client);
-
-/**
- * Attempt to connect asyncronously to the given hostname/service. Once a connection has been established, this will
- * call transport_connected(), so you can register transport_methods::_connected if layering on top of TCP.
- *
- * In case of errors while starting the async connect process, an error code will be returned. If the connect fails
- * later on, transport_connected() will be called with the error code.
- *
- * The sock must have been initialized using sock_tcp_init().
- *
- * @param client the unconnected TCP client socket to connect with
- * @param hostname the hostname to resolve
- * @param service the service to connect to
- * @param err returned error info for immediate errors
- */
-err_t tcp_client_connect_async (struct tcp_client *client, const char *hostname, const char *service, error_t *err);
-
-/**
- * Deinitialize the tcp_client's state, including the tcp_transport state.
- */
-void tcp_client_deinit (struct tcp_client *client);
-
-
-
-/**
- * TCP service type
- */
-extern const struct service_type tcp_server_type;
-
-/**
- * TCP service state
- */
-struct tcp_server {
-    /** Base service state */
-    struct service base_service;
-    
-    /** The input event with our listen() socket */
-    struct event *ev;
-};
-
-/**
- * The listen() backlog
- */
-#define TCP_SERVER_BACKLOG 5
-
-/**
- * Open the listening socket on the given interface/service.
- */
-err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err);
-
-/**
- * Release the tcp_server's state, and cleanup the struct.
- */
-void tcp_server_deinit (struct tcp_server *serv);
-
-#endif /* TCP_INTERNAL_H */
--- a/src/tcp_server.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,260 +0,0 @@
-#include "tcp_internal.h"
-#include "sock_internal.h"
-#include "log.h"
-
-#include <unistd.h>
-
-/*
- * Service methods
- */
-void tcp_server__deinit (service_t *service)
-{
-    struct tcp_server *serv = service_check(service, &tcp_server_type);
-
-    tcp_server_deinit(serv);
-}
-
-/*
- * Service type
- */
-const struct service_type tcp_server_type = {
-    .base_type = {
-        .parent = &service_type_type,
-    },
-    .methods = {
-        .deinit = tcp_server__deinit,
-    },
-};
-
-/*
- * We got a new client, build a transport for it and give it to the user
- */
-static err_t tcp_server_client (struct tcp_server *serv, evutil_socket_t sock, error_t *err)
-{
-    struct tcp_transport *trans;
-    
-    // create a new transport for it, this also makes it nonblocking
-    if (tcp_transport_create(&trans, &serv->base_service.info.trans_info, sock, err))
-        goto error;
-
-    // make it connected
-    // this will call transport_callbacks::on_connect, which is all the user needs
-    if (tcp_transport_connected(trans, err))
-        goto error;
-
-    // ok
-    return SUCCESS;
-
-error:
-    // cleanup
-    if (trans)
-        tcp_transport_destroy(trans);
-
-    return ERROR_CODE(err);    
-}
-
-/*
- * Libevent callback
- */
-static void tcp_server_on_accept (evutil_socket_t sock, short what, void *arg)
-{
-    struct tcp_server *serv = arg;
-    evutil_socket_t client_sock;
-    error_t err;
-
-    (void) what;
-
-    // accept as a new client connection
-    if ((client_sock = accept(sock, NULL, NULL)) < 0 && errno != EAGAIN)
-        JUMP_SET_ERROR_ERRNO(&err, ERR_ACCEPT);
-
-    // spurious read event?
-    if (client_sock < 0)
-        return;
-
-    // handle it
-    if (tcp_server_client(serv, client_sock, &err))
-        goto error;
-
-    // ok
-    return;
-
-error:
-    if (client_sock >= 0)
-        EVUTIL_CLOSESOCKET(client_sock);
-
-    // faaail
-    service_error(&serv->base_service, &err);
-}
-
-/*
- * Attempts to construct a listen()'d socket with the given addr, and return it
- *
- * @param addr the addrinfo to try and create a socket for
- * @param err returned error info
- * @return listening socket, or -err_t on error
- */
-static int tcp_server_sock_addr (struct addrinfo *addr, error_t *err)
-{
-    evutil_socket_t sock;
-
-    // create the sock
-    if ((sock = tcp_sock_create(addr, err)) < 0)
-        goto error;
-
-    // bind it
-    if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0)
-        JUMP_SET_ERROR_ERRNO(err, ERR_BIND);
-
-    // listen
-    if (listen(sock, TCP_SERVER_BACKLOG) < 0)
-        JUMP_SET_ERROR_ERRNO(err, ERR_LISTEN);
-    
-    // ok, valid socket
-    return sock;
-
-error:
-    if (sock >= 0)
-        // cleanup
-        EVUTIL_CLOSESOCKET(sock);
-    
-    return -ERROR_CODE(err);
-}
-
-/*
- * Construct a listen()'d socket with the given resolver result, and return it.
- *
- * @param rr the resolver lookup result to create a socket for
- * @param err returned error info
- * @return listening socket, or -err_t on error
- */
-static int tcp_server_sock (struct resolve_result *rr, error_t *err)
-{
-    struct addrinfo *addr;
-    evutil_socket_t sock;
-
-    // try each addrinfo
-    while ((addr = resolve_result_next(rr))) {
-        // attempt to construct given socket
-        if ((sock = tcp_server_sock_addr(addr, err)) < 0)
-            // log an informative error warning
-            log_warn_error(err, "%s", resolve_addr_text(addr)); 
-
-        else
-            // got a valid socket 
-            break;
-    }
-    
-    if (sock >= 0)
-        // valid socket
-        return sock;
-
-    else
-        // some error occured
-        return -ERROR_CODE(err);
-}
-
-err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err)
-{
-    struct resolve_result rr;
-    evutil_socket_t sock;
-
-    // get the global event_base
-    struct event_base *ev_base = _sock_stream_ctx.ev_base;
-
-    // init the resolver
-    resolve_result_init(&rr);
-
-    // resolve the interface/service
-    if (resolve_addr(&rr, interface, service, SOCK_STREAM, AI_PASSIVE, err))
-        return ERROR_CODE(err);
-    
-    // create the socket
-    if ((sock = tcp_server_sock(&rr, err)) < 0)
-        goto error;
-
-    // deinit lookup results
-    resolve_result_deinit(&rr);
-
-    // make it nonblocking
-    if (evutil_make_socket_nonblocking(sock))
-        JUMP_SET_ERROR_STR(err, ERR_MISC, "evutil_make_socket_nonblocking");
-    
-    // construct event for the sock
-    if ((serv->ev = event_new(ev_base, sock, EV_READ | EV_PERSIST, tcp_server_on_accept, serv)) == NULL)
-        JUMP_SET_ERROR(err, ERR_EVENT_NEW);
-
-    // add it
-    if (event_add(serv->ev, NULL))
-        JUMP_SET_ERROR(err, ERR_EVENT_ADD);
-
-    // ok
-    return SUCCESS;
-
-error:
-    // deinit results just to be sure
-    resolve_result_deinit(&rr);
-
-    if (sock >= 0 && !serv->ev)
-        // need to close socket ourselves, because we couldn't register our event for it
-        EVUTIL_CLOSESOCKET(sock);
-    
-    // general cleanup
-    tcp_server_deinit(serv);
-    
-    return ERROR_CODE(err);
-}
-
-void tcp_server_deinit (struct tcp_server *serv)
-{
-    if (serv->ev) {
-        // ignore errors
-        event_del(serv->ev);
-
-        // ignore errors
-        close(event_get_fd(serv->ev));
-        
-        // release event
-        event_free(serv->ev);
-        
-        // invalidate
-        serv->ev = NULL;
-    }
-}
-
-static void tcp_server_destroy (struct tcp_server *serv)
-{
-    tcp_server_deinit(serv);
-
-    free(serv);
-}
-
-/*
- * Public interface
- */
-err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
-        const char *interface, const char *service, error_t *err)
-{
-    struct tcp_server *serv;
-
-    // alloc
-    if ((serv = calloc(1, sizeof(*serv))) == NULL)
-        return SET_ERROR(err, ERR_MEM);
-
-    // init service
-    service_init(&serv->base_service, &tcp_server_type, info);
-
-    // init ourselves
-    if (tcp_server_listen(serv, interface, service, err))
-        goto error;
-
-    // ok
-    *service_ptr = &serv->base_service;
-
-    return SUCCESS;
-
-error:
-    tcp_server_destroy(serv);
-
-    return ERROR_CODE(err);
-}
--- a/src/tcp_transport.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-#include "tcp_internal.h"
-#include "sock_internal.h"
-
-#include <unistd.h>
-
-/*
- * Our transport_type
- */
-const struct transport_type tcp_transport_type = {
-    .base_type = {
-        .parent             = &transport_fd_type.base_type,
-    },
-    .methods                = {
-        .read               = transport_fd__read,
-        .write              = transport_fd__write,
-        .events             = transport_fd__events,
-        .deinit             = transport_fd__deinit,
-    },
-};
-
-void tcp_transport_init (struct tcp_transport *trans, evutil_socket_t sock)
-{
-    struct event_base *ev_base = _sock_stream_ctx.ev_base;
-
-    transport_fd_init(&trans->base_fd, ev_base, sock);
-}
-
-err_t tcp_transport_create (struct tcp_transport **trans_ptr, const struct transport_info *info, evutil_socket_t sock, error_t *err)
-{
-    struct tcp_transport *trans;
-
-    // alloc
-    if ((trans = calloc(1, sizeof(*trans))) == NULL)
-        JUMP_SET_ERROR(err, ERR_MEM);
-
-    // init transport
-    transport_init(&trans->base_fd.base, &tcp_transport_type, info);
-
-    // init ourselves
-    tcp_transport_init(trans, sock);
-
-    // setup the socket?
-    if (sock >= 0) {
-        // make it non-blocking
-        if ((ERROR_CODE(err) = transport_fd_nonblock(&trans->base_fd, true)))
-            goto error;
-    }
-
-    // ok
-    *trans_ptr = trans;
-
-    return SUCCESS;
-
-error:
-    // cleanup
-    if (trans)
-        tcp_transport_deinit(trans);
-    else
-        EVUTIL_CLOSESOCKET(sock);
-
-    return ERROR_CODE(err);
-}
-
-err_t tcp_transport_connected (struct tcp_transport *trans, error_t *err)
-{
-    // set up for default transport event-based operation
-    if ((ERROR_CODE(err) = transport_fd_defaults(&trans->base_fd)))
-        return ERROR_CODE(err);
-
-    // ok
-    transport_connected(&trans->base_fd.base, NULL, false);
-
-    return SUCCESS;
-}
-
-void tcp_transport_deinit (struct tcp_transport *trans)
-{
-    transport_fd_deinit(&trans->base_fd);
-}
-
-void tcp_transport_destroy (struct tcp_transport *trans)
-{
-    tcp_transport_deinit(trans);
-
-    free(trans);
-}
--- a/src/transport.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-#include "transport_internal.h"
-
-#include <assert.h>
-
-/**
- * Our own object_type
- */
-const struct object_type transport_type_type = {
-    .parent     = NULL,
-};
-
-/*
- * Internal API
- */
-void transport_init (transport_t *transport, const struct transport_type *type, const struct transport_info *info)
-{
-    // init object
-    object_init(&transport->base_obj, &type->base_type);
-    
-    // store
-    if (info)
-        transport->info = *info;
-}
-
-void* transport_check (transport_t *transport, const struct transport_type *type)
-{
-    // trip as a bug
-    assert(object_check(&transport->base_obj, &type->base_type));
-
-    // ok, cast via void*
-    return transport;
-}
-
-void transport_connected (transport_t *transport, const error_t *err, bool direct)
-{
-    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
-
-    if (direct || !type->methods._connected) {
-        // user callback
-        if (err) {
-            // connect failed
-            transport->info.cb_tbl->on_error(transport, err, transport->info.cb_arg);
-
-        } else {
-            // update state
-            transport->connected = true;
-
-            // connect succesfull
-            transport->info.cb_tbl->on_connect(transport, transport->info.cb_arg);
-        }
-
-    } else {
-        // wrapper method
-        type->methods._connected(transport, err);
-
-    }
-}
-
-void transport_invoke (transport_t *transport, short what)
-{
-    // on_ready
-    if (what & TRANSPORT_READ && transport->info.cb_tbl->on_read)
-        transport->info.cb_tbl->on_read(transport, transport->info.cb_arg);
-
-    // on_write
-    if (what & TRANSPORT_WRITE && transport->info.cb_tbl->on_write)
-        transport->info.cb_tbl->on_write(transport, transport->info.cb_arg);
-
-}
-
-void transport_error (transport_t *transport, const error_t *err)
-{
-    // update state
-    transport->connected = false;
-
-    // invoke callback
-    transport->info.cb_tbl->on_error(transport, err, transport->info.cb_arg);
-}
-
-/*
- * Public API
- */
-int transport_read (transport_t *transport, void *buf, size_t len, error_t *err)
-{
-    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
-
-    // not readable
-    if (!type->methods.read)
-        return SET_ERROR(err, -1);
-
-    // proxy off to method handler
-    if (type->methods.read(transport, buf, &len, err))
-        return -ERROR_CODE(err);
-    
-    // return updated 'bytes-read' len
-    return len;
-}
-
-int transport_write (transport_t *transport, const void *buf, size_t len, error_t *err)
-{
-    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
-
-    // XXX: not writeable
-    if (!type->methods.write)
-        return SET_ERROR(err, -1);
-
-    // proxy off to method handler
-    if (type->methods.write(transport, buf, &len, err))
-        return -ERROR_CODE(err);
-
-    // return updated 'bytes-written' len
-    return len;
-}
-
-err_t transport_events (transport_t *transport, short mask)
-{
-    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
-    error_t err;
-
-    // notify transport
-    if (type->methods.events) {
-        if (type->methods.events(transport, mask, &err))
-            goto error;
-    }
-
-    // update the event mask
-    transport->info.ev_mask = mask;
-
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(&err);
-}
-
-void transport_set_callbacks (transport_t *transport, const struct transport_callbacks *cb_tbl, void *cb_arg)
-{
-    transport->info.cb_tbl = cb_tbl;
-    transport->info.cb_arg = cb_arg;
-}
-
-void transport_destroy (transport_t *transport)
-{
-    const struct transport_type *type = object_type(&transport->base_obj, &transport_type_type);
-
-    // destroy the transport-specific stuff
-    if (type->methods.deinit)
-        type->methods.deinit(transport);
-
-    // then the transport itself
-    free(transport);
-}
-
--- a/src/transport.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-#ifndef TRANSPORT_H
-#define TRANSPORT_H
-
-/**
- * @file
- *
- * Defines a intermediate-level (as opposed to high-level or low-level) API for connected streams of data, presumeably
- * non-blocking ones.
- */
-#include "error.h"
-
-/**
- * Opaque transport state handle.
- *
- * Transports are reliable byte streams, connected with some endpoint over some medium. Common implementations are
- * e.g. TCP, SSL or fifo transports (using the OS file/socket API).
- *
- * Transports can be connected or unconnected. For synchronous opens (e.g. fifo_open_read()), the transport returned
- * will already be connected, meaning that the transport_callbacks::on_connect callback is unused. For async connects
- * such as sock_tcp_connect()/sock_ssl_connect(), the transport returned is *not* connected, and you must wait for
- * transport_callbacks::on_connect to be called before being able to send/recieve data on the transport.
- *
- * Once you have an opened transport, sending and receiving data is simple - just call transport_read()/transport_write().
- * These implement unbuffered I/O, so they may do partial reads/writes. In terms of the system read/write calls, the
- * main difference is in the error return codes. On EOF, instead of returning zero, they return ERR_EOF (or
- * ERR_WRITE_EOF for transport_write, for whoever knows what that means...). This means that when the underlying
- * transport is unable to fufill the request due to lack of data/buffer space, these can return zero to signifiy
- * something simliar to EAGAIN.
- *
- * The transport API also implements non-blocking/event-based operation (usually on top of libevent), although at a
- * slightly different level than the normal select/poll API. Instead of the user asking the transport to notify for
- * read/write after transport_read/transport_write return zero, the transport will take care of this itself. 
- * 
- * Specifically, the user can supply a mask of events they are currently interested in. By default, this should be the
- * full TRANSPORT_READ | TRANSPORT_WRITE, as the transport will take care of managing events by itself. If you wish to
- * e.g. throttle read/write, you may set a different event mask using transport_events(), which will prevent the
- * relevant callback from being triggered.
- *
- * For reads, the transport maintains a persistent read event, and will always call on_read when data is available on
- * the socket (i.e. normal select() semantics). If masked out using transport_events(), there should be no event
- * activity on the transport (i.e. the fd read event is removed).
- *
- * For writes, the transport maintains a write event that is disabled by default. If transport_write() returns zero, it will
- * become enabled *once*, and consequently trigger transport_callbacks::on_write *once*, after which you must call
- * transport_write() to possibly enable it again. If masked out using transport_events(), transport_write() will not
- * enable the write event, and any pending write event is cancelled. If masked back in using transport_events(), the
- * write event will *not* be registered, so if you have pending data, do a transport_write() after enabling
- * TRANSPORT_WRITE.
- *
- * Note that transport_write() returning fewer bytes than given will *not* enable the write event! You must call
- * transport_write() until you have either written all of your data, or it returns zero!
- */
-struct transport;
-
-/**
- * @see transport
- */
-typedef struct transport transport_t;
-
-/**
- * User callbacks for transports
- *
- * @see transport
- */
-struct transport_callbacks {
-    /**
-     * The transport is now connected
-     */
-    void (*on_connect) (transport_t *transport, void *arg);
-
-    /**
-     * Data is now available for reading from the transport
-     */
-    void (*on_read) (transport_t *transport, void *arg);
-
-    /**
-     * The transport has become writeable
-     */
-    void (*on_write) (transport_t *transport, void *arg);
-
-    /**
-     * An asynchronous error has occured. This is only called for errors that occur while being called directly from
-     * the underlying event loop, and never from inside an API function.
-     *
-     * You must call transport_destroy to release the transport.
-     */
-    void (*on_error) (transport_t *transport, const error_t *err, void *arg);
-};
-
-/**
- * Bitmask of available events
- *
- * @see transport
- */
-enum transport_event {
-    TRANSPORT_READ  = 0x01,
-    TRANSPORT_WRITE = 0x02,
-};
-
-/**
- * User info required to build a transport
- *
- * @see transport
- */
-struct transport_info {
-    /** The callbacks table */
-    const struct transport_callbacks *cb_tbl;
-
-    /** The callback context argument */
-    void *cb_arg;
-
-    /** Initial event mask using transport_event flags */
-    short ev_mask;
-};
-
-/**
- * Read a series of bytes from the transport into the given \a buf (up to \a len bytes). If succesfull, this returns
- * the number of bytes read (which will be less than or equal to \a len). If the transport is nonblocking, and there is
- * no data available, this returns zero, and need not be called again until transport_callbacks::on_read is invoked.
- *
- * On errors, this returns the negative error code, and more info via \a err. Note that as opposed to read(2), EOF is
- * handled as an error, returning ERR_EOF.
- *
- * @param transport the transport state
- * @param buf the buffer to read the bytes into
- * @param len the number of bytes to read into the buffer
- * @param err returned error info
- * @return bytes read, zero if none available, -err_t
- */
-int transport_read (transport_t *transport, void *buf, size_t len, error_t *err);
-
-/**
- * Write a series of bytes from the given \a buf (containing \a len bytes) to the transport. If succesfull, this
- * returns the number of bytes written (which may be less than \a len). If the transport is nonblocking, and the
- * operation would have blocked, no data will be written, and zero is returned; in this case, the transport's write
- * event is enabled (unless TRANSPORT_WRITE is masked out).
- *
- * On errors, this returns the negative error code, along with extended info via \a err.
- *
- * @param transport the transport state
- * @param buf the buffer to write the bytes from
- * @param len number of bytes to write
- * @param err returned error info
- * @return bytes written, zero if would have blocked, -err_t
- */
-int transport_write (transport_t *transport, const void *buf, size_t len, error_t *err);
-
-/**
- * Change the mask of enabled events.
- */
-err_t transport_events (transport_t *transport, short mask);
-
-/**
- * Install a new set of callback handlers, replacing the old ones.
- */
-void transport_set_callbacks (transport_t *transport, const struct transport_callbacks *cb_tbl, void *cb_arg);
-
-/**
- * Close and destroy the transport immediately, severing any established connection rudely.
- *
- * This will release all resources associated with the transport, including the transport itself, which must not be
- * used anymore.
- */
-void transport_destroy (transport_t *transport);
-
-#endif
--- a/src/transport_fd.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,367 +0,0 @@
-#include "transport_fd.h"
-
-#include "log.h"
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <assert.h>
-
-/**
- * Our libevent callback
- */
-static void transport_fd_on_event (evutil_socket_t _fd, short ev_what, void *arg) 
-{
-    struct transport_fd *fd = arg;
-
-    (void) _fd;
-
-    short what = 0;
-
-    // build flags
-    if (ev_what & EV_READ)
-        what |= TRANSPORT_READ;
-
-    if (ev_what & EV_WRITE)
-        what |= TRANSPORT_WRITE;
-
-    // invoke user callback
-    fd->cb_func(fd, what, fd->cb_arg);
-}
-
-/**
- * Our transport_methods implementations
- */
-err_t transport_fd__read (transport_t *transport, void *buf, size_t *len, error_t *err)
-{
-    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
-    int ret;
-
-    RESET_ERROR(err);
-    
-    // read(), and detect non-EAGAIN or EOF
-    if ((ret = read(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
-        // unexpected error
-        RETURN_SET_ERROR_ERRNO(err, ERR_READ);
-    
-    else if (ret == 0)
-        // EOF
-        return SET_ERROR(err, ERR_EOF);
-
-
-    if (ret < 0) {
-        // EAGAIN -> zero bytes
-        *len = 0;
-
-    } else {
-        // normal -> bytes read
-        *len = ret;
-    }
-
-    // ok
-    return SUCCESS;
-}
-
-err_t transport_fd__write (transport_t *transport, const void *buf, size_t *len, struct error_info *err)
-{
-    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
-    int ret;
-    
-    RESET_ERROR(err);
-    
-    // write(), and detect non-EAGAIN or EOF
-    if ((ret = write(fd->fd, buf, *len)) < 0 && errno != EAGAIN)
-        // unexpected error
-        RETURN_SET_ERROR_ERRNO(err, ERR_WRITE);
-    
-    else if (ret == 0)
-        // EOF
-        return SET_ERROR(err, ERR_WRITE_EOF);
-
-
-    if (ret < 0) {
-        // EAGAIN -> zero bytes
-        *len = 0;
-
-        if (transport->info.ev_mask & TRANSPORT_WRITE)
-            // enable the write event
-            if ((ERROR_CODE(err) = transport_fd_enable(fd, TRANSPORT_WRITE)))
-                return ERROR_CODE(err);
-
-    } else {
-        // normal -> bytes read
-        *len = ret;
-    }
-
-    return SUCCESS;
-}
-
-err_t transport_fd__events (transport_t *transport, short ev_mask, error_t *err)
-{
-    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
-    
-    short mask = 0;
-
-    // enable read as requested
-    if (ev_mask & TRANSPORT_READ)
-        mask |= TRANSPORT_READ;
-    
-    // enable write if requested and it's currently enabled
-    if ((ev_mask & TRANSPORT_WRITE) && event_pending(fd->ev_write, EV_WRITE, NULL))
-        mask |= TRANSPORT_WRITE;
-
-    // set
-    return (ERROR_CODE(err) = transport_fd_events(fd, mask));
-}
-
-void transport_fd__deinit (transport_t *transport)
-{
-    struct transport_fd *fd = transport_check(transport, &transport_fd_type);
-
-    transport_fd_deinit(fd);
-}
-
-const struct transport_type transport_fd_type = {
-    .base_type  = {
-        .parent     = &transport_type_type,
-    },
-    .methods    = {
-        .read       = transport_fd__read,
-        .write      = transport_fd__write,
-        .events     = transport_fd__events,
-        .deinit     = transport_fd__deinit
-    }
-};
-
-/**
- * Dummy callbacks
- */
-void transport_fd_callback_user (struct transport_fd *fd, short what, void *arg)
-{
-    (void) arg;
-    
-    // proxy
-    transport_invoke(TRANSPORT_FD_BASE(fd), what);
-}
-
-/**
- * Function implementations
- */
-void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd)
-{
-    // sanity-check
-    assert(!fd->fd);
-    assert(!fd->ev_read && !fd->ev_write);
-    assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0);
-
-    // initialize
-    fd->ev_base = ev_base;
-    fd->fd = _fd;
-    fd->cb_func = fd->cb_arg = NULL;
-}
-
-err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock)
-{
-    assert(fd->fd != TRANSPORT_FD_INVALID);
-
-    // XXX: maintain old flags?
-
-
-    // set new flags
-    if (fcntl(fd->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
-        return ERR_FCNTL;
-
-    return SUCCESS;
-}
-
-/**
- * Install our internal event handler.
- *
- * The events should not already be set up.
- *
- * Cleans up partial events on errors
- */
-err_t transport_fd_install (struct transport_fd *fd)
-{
-    assert(fd->fd != TRANSPORT_FD_INVALID);
-    assert(!fd->ev_read && !fd->ev_write);
-
-    // create new events
-    if ((fd->ev_read = event_new(fd->ev_base, fd->fd, EV_READ | EV_PERSIST, transport_fd_on_event, fd)) == NULL)
-        goto err_event_add;
-
-    if ((fd->ev_write = event_new(fd->ev_base, fd->fd, EV_WRITE, transport_fd_on_event, fd)) == NULL)
-        goto err_event_add;
-    
-    // ok
-    return SUCCESS;
-
-err_event_add:
-    // remove partial events
-    transport_fd_clear(fd);
-
-    return ERR_EVENT_NEW;
-}
-
-err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg)
-{
-    // requires a valid fd
-    assert(fd->fd != TRANSPORT_FD_INVALID);
-    
-    // store
-    fd->cb_func = cb_func;
-    fd->cb_arg = cb_arg;
-    
-    // install the event handlers?
-    if (!fd->ev_read || !fd->ev_write)
-        return transport_fd_install(fd);
-    else
-        return SUCCESS;
-}
-
-err_t transport_fd_enable (struct transport_fd *fd, short mask)
-{
-    // just add the appropriate events
-    if (mask & TRANSPORT_READ && event_add(fd->ev_read, NULL))
-        return ERR_EVENT_ADD;
- 
-    if (mask & TRANSPORT_WRITE && event_add(fd->ev_write, NULL))
-        return ERR_EVENT_ADD;
-    
-
-    return SUCCESS;
-}
-
-err_t transport_fd_disable (struct transport_fd *fd, short mask)
-{
-    if (mask & TRANSPORT_READ && event_del(fd->ev_read))
-        return ERR_EVENT_DEL;
-
-    if (mask & TRANSPORT_WRITE && event_del(fd->ev_write))
-        return ERR_EVENT_DEL;
-
-
-    return SUCCESS;
-}
-
-err_t transport_fd_events (struct transport_fd *fd, short mask)
-{
-    err_t err;
-    
-    // enable/disable read
-    if (mask & TRANSPORT_READ)
-        err = event_add(fd->ev_read, NULL);
-    else
-        err = event_del(fd->ev_read);
-
-    if (err)
-        return err;
-
-    // enable/disable write
-    if (mask & TRANSPORT_WRITE)
-        err = event_add(fd->ev_write, NULL);
-    else
-        err = event_del(fd->ev_write);
-
-    if (err)
-        return err;
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Remove our current ev_* events, but leave the cb_* intact.
- */
-static void transport_fd_remove (struct transport_fd *fd)
-{
-    if (fd->ev_read)
-        event_free(fd->ev_read);
-
-    if (fd->ev_write)
-        event_free(fd->ev_write);
-    
-    fd->ev_read = NULL;
-    fd->ev_write = NULL;
-}
-
-void transport_fd_clear (struct transport_fd *fd)
-{
-    // remove the events
-    transport_fd_remove(fd);
-    
-    // clear the callbacks
-    fd->cb_func = fd->cb_arg = NULL;
-}
-
-err_t transport_fd_defaults (struct transport_fd *fd)
-{
-    error_t err;
-
-    // install the transport_invoke callback handler
-    if ((ERROR_CODE(&err) = transport_fd_setup(fd, transport_fd_callback_user, NULL)))
-        goto error;
-
-    // enable read unless masked out
-    if (TRANSPORT_FD_BASE(fd)->info.ev_mask & TRANSPORT_READ) {
-        if ((ERROR_CODE(&err) = transport_fd_enable(fd, TRANSPORT_READ)))
-            goto error;
-    }
-    
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(&err);
-}
-
-err_t transport_fd_set (struct transport_fd *fd, int _fd)
-{
-    assert(_fd == TRANSPORT_FD_INVALID || _fd >= 0);
-
-    // close the old stuff
-    transport_fd_close(fd);
-    
-    // set the new one
-    fd->fd = _fd;
-    
-    // do we have callbacks that we need to setup?
-    if (fd->cb_func)
-        return transport_fd_install(fd);
-    else 
-        return SUCCESS;
-}
-
-void transport_fd_invoke (struct transport_fd *fd, short what)
-{
-    // invoke
-    transport_invoke(TRANSPORT_FD_BASE(fd), what);
-}
-
-err_t transport_fd_close (struct transport_fd *fd)
-{
-    int _fd = fd->fd;
-
-    // remove any installed events
-    transport_fd_remove(fd);
-
-    // invalidate fd
-    fd->fd = TRANSPORT_FD_INVALID;
- 
-    // close the fd
-    if (_fd != TRANSPORT_FD_INVALID && close(_fd))
-        return ERR_CLOSE;
-
-
-    return SUCCESS;
-}
-
-void transport_fd_deinit (struct transport_fd *fd)
-{
-    err_t tmp;
-
-    // XXX: this might block
-    if ((tmp = transport_fd_close(fd)))
-        log_warn_err(tmp, "close");
-
-}
-
--- a/src/transport_fd.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-#ifndef TRANSPORT_FD_H
-#define TRANSPORT_FD_H
-
-/**
- * @file
- *
- * Support for transport implementations that use POSIX file descriptor streams.
- *
- * This provides the read/write methods, as well as functions to implement the event-based behaviour.
- */
-#include "transport_internal.h"
-
-#include <event2/event.h>
-#include <stdbool.h>
-
-// forward-declare
-struct transport_fd;
-
-/**
- * Our transport_type
- */
-extern const struct transport_type transport_fd_type;
-
-/**
- * Low-level callback
- */
-typedef void (*transport_fd_callback_func) (struct transport_fd *fd, short what, void *arg);
-
-/**
- * The fd-based transport implementation
- */
-struct transport_fd {
-    /** Base transport state */
-    struct transport base;
-
-    /** Libevent base to use */
-    struct event_base *ev_base;
-    
-    /** OS file descriptor */
-    evutil_socket_t fd;
-
-    /** IO events */
-    struct event *ev_read, *ev_write;
-
-    /** Low-level callback */
-    transport_fd_callback_func cb_func;
-
-    /** Callback context argument */
-    void *cb_arg;
-
-};
-
-/**
- * Get a transport_t pointer from a transport_fd
- */
-#define TRANSPORT_FD_BASE(tp_ptr) (&(tp_ptr)->base)
-
-/**
- * Invalid OS FD
- */
-#define TRANSPORT_FD_INVALID ((evutil_socket_t) -1)
-
-/**
- * Implementation of transport_methods::read
- */
-err_t transport_fd__read (transport_t *transport, void *buf, size_t *len, error_t *err);
-
-/**
- * Implementation of transport_methods::write.
- *
- * If this gets EAGAIN, it will automatically enable the write event, unless masked out.
- */
-err_t transport_fd__write (transport_t *transport, const void *buf, size_t *len, error_t *err);
-
-/**
- * Implementation of transport_methods::events.
- *
- * For TRANSPORT_READ, this will simply apply enable/disable as given.
- *
- * For TRANSPORT_WRITE, the write event will only be enabled if given in the mask, *and* the ev_write event is currently
- * active (via transport_fd_methods_write()); otherwise, the write event will not be enabled.
- */
-err_t transport_fd__events (transport_t *transport, short mask, error_t *err);
-
-/**
- * Implementation of transport_methods::deinit.
- *
- * This simply calls transport_fd_deinit().
- */
-void transport_fd__deinit (transport_t *transport);
-
-/**
- * A transport_fd_callback_func that simply invokes the transport_callback user functions.
- *
- * Register with a NULL cb_arg.
- */
-void transport_fd_callback_user (struct transport_fd *fd, short what, void *arg);
-
-
-
-
-/**
- * Initialize the transport_fd to use the given, connected fd, or TRANSPORT_FD_INVALID if we don't yet have an fd.
- *
- * It is an error to call this if the transport_fd already has an fd set
- *
- * @param fd the transport_fd state
- * @param ev_base the libevent base to use
- * @param _fd the OS file descriptor, or TRANSPORT_FD_INVALID
- */
-void transport_fd_init (struct transport_fd *fd, struct event_base *ev_base, int _fd);
-
-/**
- * Set the fd's nonblocking mode using fcntl.
- */
-err_t transport_fd_nonblock (struct transport_fd *fd, bool nonblock);
-
-/**
- * Set or replace the fd's event callback. The callback will not be enabled.
- *
- * The transport must have a valid fd bound to it.
- */
-err_t transport_fd_setup (struct transport_fd *fd, transport_fd_callback_func cb_func, void *cb_arg);
-
-/**
- * Enable the specified events, any of { TRANSPORT_READ, TRANSPORT_WRITE }.
- */
-err_t transport_fd_enable (struct transport_fd *fd, short mask);
-
-/**
- * Disable the specified events, any of { TRANSPORT_READ, TRANSPORT_WRITE }.
- */
-err_t transport_fd_disable (struct transport_fd *fd, short mask);
-
-/**
- * Set the enable/disable state of our events to the given mask.
- */
-err_t transport_fd_events (struct transport_fd *fd, short mask);
-
-/**
- * Remove any old event callback present, so it will not be called anymore.
- *
- * It is perfectly safe to call this without any callbacks installed.
- */
-void transport_fd_clear (struct transport_fd *fd);
-
-/**
- * Setup and enable the default event handlers for transport operation, that is, use transport_fd_callback_user as the 
- * callback and enable TRANSPORT_READ, unless masked out.
- */
-err_t transport_fd_defaults (struct transport_fd *fd);
-
-/**
- * Replace the old fd with a new one, maintaining any event callbacks set with transport_fd_callback. If any events were
- * enabled before, they are not enabled anymore.
- */
-err_t transport_fd_set (struct transport_fd *fd, int _fd);
-
-/**
- * Close an opened fd, releasing all resources within our state.
- */
-err_t transport_fd_close (struct transport_fd *fd);
-
-/**
- * Deinitialize the transport_fd.
- *
- * This logs a warning if the close() fails.
- *
- * XXX: this may actually block, I think? SO_LINGER?
- */
-void transport_fd_deinit (struct transport_fd *fd);
-
-#endif
--- a/src/transport_internal.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-#ifndef TRANSPORT_INTERNAL_H
-#define TRANSPORT_INTERNAL_H
-
-/**
- * @file
- *
- * The internal interface for transport implementations.
- */ 
-#include "transport.h"
-#include "object.h"
-
-#include <stdbool.h>
-
-/**
- * The object_type for a transport_type
- */
-extern const struct object_type transport_type_type;
-
-/**
- * The definition of a transport type
- */
-struct transport_type {
-    struct object_type base_type;
-
-    /**
-     * Method table for implementation stuff.
-     *
-     * Note that it is the transport's resposibility to implement the behaviour described in transport.h
-     */
-    struct transport_methods {
-        /** For transport_read() */
-        err_t (*read) (transport_t *transport, void *buf, size_t *len, error_t *err);
-
-        /** For transport_write() */
-        err_t (*write) (transport_t *transport, const void *buf, size_t *len, error_t *err);
-
-        /**
-         * The mask of event flags will be set to the given mask if this method is succesfull.
-         *
-         * The old mask is still available in transport::info::ev_mask.
-         */
-        err_t (*events) (transport_t *transport, short mask, error_t *err);
-
-        /** 
-         * Release the transport's internal state, but not the transport itself.
-         *
-         * In other words, this should release everything inside the transport_t, but not free() the transport_t itself.
-         */
-        void (*deinit) (transport_t *transport);
-
-        /**
-         * Used by layered transports to handle transport_connected.
-         *
-         * If this is NULL, transport_connected will call the user callback directly, otherwise, it will proxy through this.
-         *
-         * The \a err param follows the same rules as for transport_connected() - NULL for success, error info otherwise.
-         *
-         * @param transport the transport state
-         * @param err error info if the connect failed
-         */
-        void (*_connected) (transport_t *transport, const error_t *err);
-    } methods;
-};
-
-/**
- * The base transport type
- */
-struct transport {
-    struct object base_obj;
-
-    /** User info */
-    struct transport_info info;
-    
-    /** Are we connected? */
-    bool connected;
-};
-
-/**
- * Bind the given transport to the given type with the given user info.
- *
- * \a info may be given as NULL to not have any callbacks, but this will crash if any transport_* is called before
- * transport_set_callbacks().
- *
- * It is a bug to call this with a transport that is already bound.
- */
-void transport_init (transport_t *transport, const struct transport_type *type, const struct transport_info *info);
-
-/**
- * Check the type of the transport, and return the transport as a void* suitable for casting to the appropriate struct
- * for the type, or any of its children.
- *
- * It is a bug to call this with a transport of a different type.
- */
-void* transport_check (transport_t *transport, const struct transport_type *type);
-
-/**
- * Mark the transport as connected, calling transport_methods::_connected if it exists and \a direct is not given,
- * transport_callbacks::on_connected/transport_callbacks::on_error otherwise.
- *
- * If the connect succeeded, \a err should be given as NULL. If the connect failed, \a err should contain the error
- * info.
- *
- * If called from the transport_methods::_connected method, pass in direct to avoid recursion.
- *
- * This sets the transport::connected flag before calling transport_callbacks::on_connected (i.e. directly) without any
- * error set.
- *
- * XXX: implement proper layering of types by taking a transport_type arg and chaining down from there.
- *
- * @param transport the transport state
- * @param err NULL for success, otherwise connect error code
- * @param direct call the user callback directly, ignoring any method
- */
-void transport_connected (transport_t *transport, const error_t *err, bool direct);
-
-/**
- * Invoke the user callbacks based on the given TRANSPORT_* flags
- */
-void transport_invoke (transport_t *transport, short what);
-
-/**
- * Mark the transport as failed, calling transport_methods::on_error with the given error code.
- */
-void transport_error (transport_t *transport, const error_t *err);
-
-#endif /* TRANSPORT_INTERNAL_H */
--- a/src/transport_test.c	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,366 +0,0 @@
-#include "transport_test.h"
-#include "transport_internal.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <assert.h>
-
-/**
- * Simple IO vector
- */
-struct io_vec {
-    /** The buffer */
-    char *buf;
-
-    /** Buffer size */
-    size_t len;
-};
-
-/**
- * Simple vectored IO-buffer
- */
-struct io_buf {
-    /** The array of buffer-vectors, {NULL}-terminated */
-    struct io_vec *vecs;
-
-    /** The number of io_vecs */
-    size_t count;
-
-    /** Current read/write vector */
-    struct io_vec *read_vec, *write_vec;
-
-    /** Offset into current vector */
-    size_t off;
-};
-
-/**
- * Forward-declare our transport_type
- */
-extern const struct transport_type transport_test_type;
-
-
-/**
- * A dummy sock_stream implementation intended for testing purposes.
- */
-struct transport_test {
-    /** The base transport stuff */
-    struct transport base;
-
-    /** The send/recieve buffers */
-    struct io_buf send_buf, recv_buf;
-
-    /** No more data is going to be added, return EOF once all the rest is consumed */
-    bool eof;
-};
-
-/**
- * Get a transport pointer from a transport_test pointer
- */
-#define TRANSPORT_TEST_BASE(tp_ptr) (&(tp_ptr)->base)
-
-/**
- * Grow buf->vecs if needed to ensure that buf->write_vec points to a valid io_vec
- */
-static err_t io_buf_grow (struct io_buf *buf)
-{
-    size_t read_vec_offset = buf->read_vec ? (buf->read_vec - buf->vecs) : 0;
-    size_t write_vec_offset = buf->write_vec ? (buf->write_vec - buf->vecs) : 0;
-    struct io_vec *v;
-    struct io_vec *vecs_tmp = buf->vecs;
-
-    // don't grow if not full
-    if (buf->vecs && buf->write_vec < buf->vecs + buf->count)
-        return SUCCESS;
-
-    // new size
-    buf->count = buf->count * 2 + 1;
-
-    // grow
-    if ((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec))) == NULL) {
-        // restore old value
-        buf->vecs = vecs_tmp;
-
-        return ERR_CALLOC;
-    }
-
-    // restore vec positions
-    buf->write_vec = buf->vecs + write_vec_offset;
-    buf->read_vec = buf->vecs + read_vec_offset;
-
-    // zero new vecs
-    for (v = buf->write_vec; v < buf->vecs + buf->count; v++)
-        memset(v, 0, sizeof(*v));
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Write some data to an io_buf, copying it.
- */
-static err_t io_buf_write (struct io_buf *buf, const char *data, size_t len)
-{
-    error_t err;
-
-    // ensure there's room
-    if ((ERROR_CODE(&err) = io_buf_grow(buf)))
-        goto error;
-    
-    // the vector to use
-    struct io_vec *vec = buf->write_vec;
-
-    // allocate
-    if ((vec->buf = malloc(len)) == NULL)
-        JUMP_SET_ERROR(&err, ERR_MEM);
-    
-    // store
-    vec->len = len;
-    memcpy(vec->buf, data, len);
-
-    // vec consumed
-    buf->write_vec++;
-
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(&err);
-}
-
-/**
- * Destroy the io_buf, freeing all resources.
- *
- * The io_buf must not be used anymore.
- */
-static void io_buf_destroy (struct io_buf *buf)
-{
-    size_t i;
-
-    // free the io_vec buffers
-    for (i = 0; i < buf->count; i++) {
-        free(buf->vecs[i].buf);
-    }
-    
-    // free the vector list
-    free(buf->vecs);
-}
-
-/**
- * transport_methods::read implementation.
- */
-static err_t transport_test__read (transport_t *transport, void *buf_ptr, size_t *len, error_t *err)
-{
-    struct transport_test *tp = transport_check(transport, &transport_test_type);
-    struct io_buf *buf = &tp->recv_buf;
-    struct io_vec *vec = buf->read_vec;
-    
-    // EOF/nonblock if we're past the end of the last vector
-    if (!vec || vec == buf->vecs + buf->count || buf->off >= vec->len) {
-        if (!tp->eof) {
-            // wait for more to be fed in
-            *len = 0;
-            return SUCCESS;
-
-        } else {
-            // EOF!
-            return SET_ERROR(err, ERR_EOF);
-        }
-    }
-    
-    // amount of data available in this iovec
-    size_t available = vec->len - buf->off;
-
-    // amount to read
-    size_t to_read = *len;
-    
-    // trim down?
-    if (to_read > available)
-        to_read = available;
-
-    // copy
-    memcpy(buf_ptr, vec->buf + buf->off, to_read);
-    
-
-    if (to_read < available) {
-        // bytes still left in the vector
-        buf->off += to_read;
-
-    } else {
-        // consumed the whole vector
-        // XXX: release data?
-        buf->read_vec++;
-        buf->off = 0;
-    }
-
-    // update len
-    *len = to_read;
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * transport_methods::write implementation.
- */
-static err_t transport_test__write (transport_t *transport, const void *data, size_t *len, error_t *err)
-{
-    struct transport_test *tp = transport_check(transport, &transport_test_type);
-
-    // write it out
-    // XXX: partial writes?
-    if ((ERROR_CODE(err) = io_buf_write(&tp->send_buf, data, *len)))
-        goto error;
-    
-    // ok
-    return SUCCESS;
-
-error:
-    return ERROR_CODE(err);
-}
-
-static err_t transport_test__events (transport_t *transport, short mask, error_t *err)
-{
-    struct transport_test *tp = transport_check(transport, &transport_test_type);
-
-    (void) tp;
-    (void) mask;
-    (void) err;
-    
-    // XXX: don't re-trigger anything
-    
-    return SUCCESS;
-}
-
-static void transport_test__deinit (transport_t *transport)
-{
-    struct transport_test *tp = transport_check(transport, &transport_test_type);
-
-    transport_test_destroy(tp);
-}
-
-/*
- * Our sock_stream_type
- */
-const struct transport_type transport_test_type = {
-    .base_type = {
-        .parent     = &transport_type_type,
-    },
-    .methods                = {
-        .read       = transport_test__read,
-        .write      = transport_test__write,
-        .events     = transport_test__events,
-        .deinit     = transport_test__deinit
-    },
-};
-
-struct transport_test* transport_test_create (struct transport_info *info)
-{
-    struct transport_test *tp;
-
-    // allocate
-    assert((tp = calloc(1, sizeof(*tp))));
-    
-    // initialize base with our transport_type
-    transport_init(TRANSPORT_TEST_BASE(tp), &transport_test_type, info);
-
-    // ok
-    return tp;
-}
-
-transport_t* transport_test_cast (struct transport_test *tp)
-{
-    return TRANSPORT_TEST_BASE(tp);
-}
-
-void transport_test_event (struct transport_test *tp, short what)
-{
-    // invoke, masking out as needed
-    // this won't do anything if all the bits are masked out
-    transport_invoke(TRANSPORT_TEST_BASE(tp), what & TRANSPORT_TEST_BASE(tp)->info.ev_mask);
-}
-
-void transport_test_push_buf (struct transport_test *tp, const char *data, size_t len)
-{
-    // push it
-    assert(io_buf_write(&tp->recv_buf, data, len) == SUCCESS);
-    
-    // notify
-    transport_test_event(tp, TRANSPORT_READ);
-}
-
-void transport_test_push_str (struct transport_test *tp, const char *str)
-{
-    // push it
-    transport_test_push_buf(tp, str, strlen(str));
-}
-
-void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...)
-{
-    char buf[TRANSPORT_TEST_FMT_MAX];
-    size_t ret;
-
-    // format
-    va_list vargs; va_start(vargs, fmt);
-    assert((ret = vsnprintf(buf, sizeof(buf), fmt, vargs)) <= sizeof(buf));
-    va_end(vargs);
-       
-    // push it
-    transport_test_push_buf(tp, buf, ret);
-}
-
-void transport_test_push_eof (struct transport_test *tp)
-{
-    // update state
-    tp->eof = true;
-
-    transport_test_event(tp, TRANSPORT_READ);
-}
-
-void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr)
-{
-    struct io_buf *buf = &tp->send_buf;
-    size_t len = 0, i, off = 0;
-    char *out;
-    
-    // calculate total size
-    for (i = 0; i < buf->count; i++) {
-        len += buf->vecs[i].len;
-    }
-
-    // alloc
-    assert((out = malloc(len)));
-
-    // copy
-    for (i = 0; i < buf->count; i++) {
-        struct io_vec *vec = buf->vecs + i;
-
-        memcpy(out + off, vec->buf, vec->len);
-        off += vec->len;
-        
-        // zero
-        free(vec->buf); vec->buf = NULL;
-        vec->len = 0;
-    }
-    
-    // update return
-    *buf_ptr = out;
-    *len_ptr = len;
-
-    // update write_vec
-    buf->write_vec = buf->vecs;
-}
-
-void transport_test_async_error (struct transport_test *tp, const error_t *err)
-{
-    transport_error(&tp->base, err);
-}
-
-void transport_test_destroy (struct transport_test *tp)
-{
-    // free the buffers
-    io_buf_destroy(&tp->send_buf);
-    io_buf_destroy(&tp->recv_buf);
-}
-
--- a/src/transport_test.h	Thu May 28 00:35:02 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-#ifndef TRANSPORT_TEST_H
-#define TRANSPORT_TEST_H
-
-/**
- * @file
- *
- * Dummy transport implemention for local testing.
- */
-#include "transport.h"
-
-/**
- * The opaque transport state
- */
-struct transport_test;
-
-/**
- * Construct a new, empty, connected transport_test.
- */
-struct transport_test* transport_test_create (struct transport_info *info);
-
-/**
- * A transport_test is a valid transport, this performs the cast
- */
-transport_t* transport_test_cast (struct transport_test *tp);
-
-/**
- * Invoke the transport's user callbacks for the given event mask, unless masked out.
- */
-void transport_test_event (struct transport_test *tp, short what);
-
-/**
- * Adds a data buffer to the recieve buffer.
- *
- * The given data is copied.
- *
- * If events are enabled, they are triggered.
- */
-void transport_test_push_buf (struct transport_test *tp, const char *buf, size_t len);
-
-/**
- * Add a string to the recieve buffer using transport_test_push_buf()
- */
-void transport_test_push_str (struct transport_test *tp, const char *str);
-
-/**
- * Maximum length of a formatted string pushed
- */
-#define TRANSPORT_TEST_FMT_MAX 4096
-
-/**
- * Add a formatted string to the recieve buffer
- *
- * @see TRANSPORT_TEST_FMT_MAX
- */
-void transport_test_push_fmt (struct transport_test *tp, const char *fmt, ...);
-
-/**
- * Set EOF on recv.
- */
-void transport_test_push_eof (struct transport_test *tp);
-
-/**
- * Get the send buffer contents as a single buffer, free() after use.
- *
- * This clears the send buffer, so this doesn't return the same data twice.
- */
-void transport_test_pull_buf (struct transport_test *tp, char **buf_ptr, size_t *len_ptr);
-
-/**
- * Send async error
- */
-void transport_test_async_error (struct transport_test *tp, const error_t *err);
-
-/**
- * Destroy the transport buffer, releasing any buffers we allocated ourself
- */
-void transport_test_destroy (struct transport_test *tp);
-
-#endif /* TRANSPORT_TEST_H */