Initial commit
authorMyhailo Danylenko <isbear@ukrpost.net>
Sun, 01 Feb 2009 21:28:57 +0200
changeset 0 84fdfb0344c9
child 1 64a857d6b81b
Initial commit * It works * Still need to debug objects collection
.libs/lm.lua
Makefile
README
TODO
docgen.pl
glib.c
glib_io.c
glib_main_context.c
glib_main_context.h
glib_source.c
glib_source.h
glib_timeout.c
glib_timeout.h
glib_types.c
glib_types.h
lm.c
lm.lua
lm_connection.c
lm_connection.h
lm_message.c
lm_message.h
lm_message_handler.c
lm_message_handler.h
lm_message_node.c
lm_message_node.h
lm_proxy.c
lm_proxy.h
lm_ssl.c
lm_ssl.h
lm_types.c
lm_types.h
util.c
util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.libs/lm.lua	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,1 @@
+../lm.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,100 @@
+
+# TODO:
+#   clarify situation with references. should we really reference objects, passed as args to callbacks? if someone will store that object in a global environment and use later?
+#   autotools
+#   switch to common object struct?
+#   tests?
+#   more examples
+#   llm should use lua/stdlib, no dependency on glib? though it still needs glib headers... maybe, indirectly :/
+#   separate lm and glib, now it is completely different projects // and one, probably, will die... :(
+
+PREFIX=${PWD}/inst
+DOCDIR=$(PREFIX)/share/doc/liblua-loudmouth
+SOLIBDIR=$(PREFIX)/lib/lua/5.1
+LUALIBDIR=$(PREFIX)/share/lua/5.1
+
+
+CC=gcc
+LD=gcc
+LIBTOOL=libtool
+INSTALL=install
+
+CFLAGS=-g -O0 -Wall
+LDFLAGS=-g -O0
+LTFLAGS=
+TARGETS=loudmouth.la glib.la
+
+LMCFLAGS=`pkg-config --cflags lua5.1 loudmouth-1.0 glib-2.0`
+GLIBCFLAGS=`pkg-config --cflags lua5.1 glib-2.0`
+
+# add this flag to cflags, if your loudmouth have lm_connection_get_keep_alive_rate ()
+#LMCFLAGS += -DHAVE_LM_CONNECTION_GET_KEEP_ALIVE_RATE
+
+LMLIBS=`pkg-config --libs lua5.1 loudmouth-1.0 glib-2.0`
+GLIBLIBS=`pkg-config --libs lua5.1 glib-2.0`
+
+LMSOURCES=util.c lm_types.c lm_proxy.c lm_ssl.c lm_connection.c lm_message.c lm_message_node.c lm_message_handler.c lm.c
+GLIBSOURCES=glib.c glib_types.c glib_main_context.c glib_source.c glib_timeout.c util.c
+
+LMOBJECTS=$(LMSOURCES:.c=.lo)
+GLIBOBJECTS=$(GLIBSOURCES:.c=.lo)
+
+DOCS=$(TARGETS:.la=.html)
+
+all: $(TARGETS)
+
+loudmouth.la: $(LMOBJECTS)
+	$(LIBTOOL) $(LTFLAGS) --mode=link $(LD) $(LDFLAGS) -module -rpath $(SOLIBDIR) -o $@ $^ $(LMLIBS)
+
+glib.la: $(GLIBOBJECTS)
+	$(LIBTOOL) $(LTFLAGS) --mode=link $(LD) $(LDFLAGS) -module -rpath $(SOLIBDIR) -o $@ $^ $(GLIBLIBS)
+
+# FIXME: CFLAGS...
+%.lo: %.c
+	$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) $(CFLAGS) $(LMCFLAGS) -c $^
+
+test: loudmouth.la glib.la
+	cd .libs && lua ../test.lua
+
+test2: glib.la
+	cd .libs && lua ../test2.lua
+
+doc: $(DOCS)
+
+loudmouth.html: docgen.pl $(LMSOURCES)
+	./docgen.pl $(LMSOURCES) > $@
+
+glib.html: docgen.pl $(GLIBSOURCES)
+	./docgen.pl $(GLIBSOURCES) > $@
+
+install: $(TARGETS) $(SOLIBDIR) $(LUALIBDIR)
+	$(LIBTOOL) $(LTFLAGS) --mode=install $(INSTALL) loudmouth.la $(SOLIBDIR)/loudmouth.la
+	$(INSTALL) -m 644 lm.lua $(LUALIBDIR)/lm.lua
+	$(LIBTOOL) $(LTFLAGS) --mode=install $(INSTALL) glib.la $(SOLIBDIR)/glib.la
+	$(LIBTOOL) $(LTFLAGS) --mode=finish $(SOLIBDIR)
+
+install-doc: doc test.lua $(DOCDIR)
+	$(INSTALL) -m 644 -t $(DOCDIR) $(DOCS)
+	$(INSTALL) -d $(DOCDIR)/examples
+	$(INSTALL) -m 644 -t $(DOCDIR)/examples test.lua
+
+$(PREFIX):
+	$(INSTALL) -D -d $@
+
+$(DOCDIR): $(PREFIX)
+	$(INSTALL) -D -d $@
+
+$(SOLIBDIR): $(PREFIX)
+	$(INSTALL) -D -d $@
+
+$(LUALIBDIR): $(PREFIX)
+	$(INSTALL) -D -d $@
+
+uninstall:
+	$(LIBTOOL) $(LTFLAGS) --mode=uninstall rm -f $(SOLIBDIR)/loudmouth.la
+	$(LIBTOOL) $(LTFLAGS) --mode=uninstall rm -f $(SOLIBDIR)/glib.la
+	rm -f $(LUALIBDIR)/lm.lua
+
+clean:
+	$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f $(TARGETS) $(LMOBJECTS) $(GLIBOBJECTS) $(DOCS)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,20 @@
+
+This is a lua 5.1 interface for loudmouth jabber client library.
+To install it, you need glib and loudmouth headers and libraries.
+Edit Makefile, set PREFIX, SOLIBDIR, LUALIBDIR, then run
+make
+make install
+
+Optionally you can generate and install api documentation (for now
+there are no lua convenience interface documentation, only for low-level
+interface, implemented in c, see example in test.lua). You will need a
+perl interpreter for that. Edit Makefile, set DOCDIR, then run
+make doc
+make doc-install
+
+This code underlies terms of GNU GPL v3 or later.
+
+I will be happy to get feedback, patches, suggestions, etc.
+
+Myhailo Danylenko <isbear@ukrpost.net>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TODO	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,5 @@
+
+* Add debugging output and verify refcounts of lm objects. Need a decent test script for that...
+* How to report errors in callbacks? We cannot use lua_error, it can be even outside of any lua pcalled environment.
+* Some additional lua functions?
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docgen.pl	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,81 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+my %docs;
+my @tags;
+my $inside;
+
+foreach my $file (@ARGV) {
+	if ( not open SOURCE, '<', $file ) {
+		print STDERR "Cannot open $file\n";
+		next;
+	}
+
+	my $chunk = 0;
+
+	while (<SOURCE>) {
+		if ( $inside ) {
+			if ( not /^\/\/\// ) {
+				$inside = 0;
+				$chunk++;
+			} else {
+				push @{$docs{$file}[$chunk]}, substr ( $_, 4 );
+			}
+		} else {
+			next if not /^\/\/\//;
+
+			$inside = 1;
+			my $tag = substr $_, 4;
+			chomp $tag;
+			push @{$docs{$file}[$chunk]}, $tag;
+			# hack to allow twoword objects be written in text with spaces
+			# now it matches "lm message" instead of "lm message node" -.-
+			# and even if tag list will be reverse sorted by length,
+			# it will produce nested links...
+			# well, that all is now solved, but in not too impressive way..
+			$tag =~ s/_/./g;
+			push @tags, $tag;
+		}
+	}
+	
+	close SOURCE;
+}
+
+print <<HEADER
+<html>
+<head><title>lua-loudmouth docs</title></head>
+<body>
+HEADER
+;
+
+@tags = reverse sort { length $a <=> length $b } @tags;
+# TODO preserve original order
+foreach my $file ( sort keys %docs ) {
+	print "<hr>";
+	foreach my $chunk ( @{$docs{$file}} ) {
+		my $head = shift @$chunk;
+		my $tag  = $head;
+		$tag =~ s/_/./g;
+		print "<a name='$tag'></a><h2>$head</h2><p>";
+		foreach ( @$chunk ) {
+			s/^A: /<br\/>Arguments: /;
+			s/^R: /<br\/>Return values: /;
+			s/^V: /<br\/>Values: /;
+			foreach my $tag ( @tags ) {
+				# TODO quotemeta required, but for now
+				# this bug is rather desired...
+				#s/\b$tag\b/<a href="#$tag">$&<\/a>/g;
+				s/(.)\b($tag)\b/ if ( $1 eq '#' or $1 eq '>' ) { "$1$2" } else { "$1<a href='#$tag'>$2<\/a>" } /ge;
+			}
+			print $_;
+		}
+		print "</p>"
+	}
+	print "<hr>";
+}
+
+print "</body></html>"
+
+# The end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,31 @@
+
+#include <lua.h>
+
+#include "glib_types.h"
+#include "glib_main_context.h"
+#include "glib_timeout.h"
+#include "glib_source.h"
+
+int luaopen_glib (lua_State *L)
+{
+	lua_pushstring (L, LGLIB_OBJREGISTRY);
+	lua_newtable (L);
+	lua_createtable (L, 0, 1);
+	lua_pushstring (L, "__mode");
+	lua_pushstring (L, "v");
+	lua_settable (L, -3);
+	lua_setmetatable (L, -2);
+	lua_rawset (L, LUA_REGISTRYINDEX);
+
+	lua_createtable (L, 3, 0);
+	lua_pushvalue (L, -1);
+	lua_setglobal (L, "g");
+
+	luaopen_glib_main_context (L);
+	luaopen_glib_source (L);
+	luaopen_glib_timeout (L);
+	lua_pop (L, 3);
+
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_io.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,78 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+#include <stdio.h>    // popen, pclose, fileno
+
+#include "util.h"
+#include "glib_types.h"
+
+static gchar lglib_io_buffer[128];
+
+void lglib_io_callback_destroy (lglib_io_callback_t *cb)
+{
+	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	pclose (cb->fd);
+	luaL_free (cb->L, cb);
+}
+
+// data/nil
+static gboolean lglib_io_callback (GIOChannel *source, GIOCondition *condition, lglib_io_callback_t *cb)
+{
+	gsize read = 0;
+	int status;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	status = g_io_channel_read_chars (source, lglib_io_buffer, 128, &read, NULL);
+	if (read) {
+		lua_pushlstring (cb->L, lglib_io_buffer, read);
+	} else if (status == G_IO_STATUS_EOF) {
+		lua_pushnil (cb->L);
+	} else {
+		return status != G_IO_STATUS_ERROR;
+	}
+	if (lua_pcall (cb->L, 1, 1, 0))
+		lua_error (cb->L);
+	return lua_toboolean (cb->L, -1);
+}
+
+// command function
+static int lglib_io_new (lua_State *L)
+{
+	const char *command = luaL_checkstring (L, 1);
+	lglib_io_callback_t *cb;
+	FILE *fd;
+	GIOChannel *channel;
+	//const char *charset;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	fd = popen (command, "r");
+	if (!fd) {
+		lua_pushstring (L, "Error opening pipe");
+		lua_error (L);
+	}
+	channel = g_io_channel_unix_new (fileno (fd));
+	//if (!g_get_charset (&charset))
+	//	g_io_channel_set_encoding (channel, charset, NULL);
+
+	cb = luaL_malloc (L, sizeof (lglib_io_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+	cb->fd        = fd;
+
+	g_io_add_watch_full (channel, G_PRIORITY_HIGH_IDLE, G_IO_IN,
+				(GSourceFunc)lglib_io_callback, cb,
+				(GDestroyNotify)lglib_io_callback_destroy);
+	return 0;
+}
+
+static const luaL_Reg lglib_io_reg_f[] = {
+	{ "new", lglib_io_new },
+	{ NULL,  NULL         },
+};
+
+int luaopen_glib_io (lua_State *L)
+{
+	luaL_register (L, "g.io", lglib_io_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_main_context.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,248 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+
+#include "glib_types.h"
+
+/// g.main_context
+/// Glib main context object. Mainly useful when lua needs to
+/// manage glib-based libs.
+
+/// g.main_context.new
+/// Creates new main context object.
+/// R: g main context object
+static int lglib_main_context_new (lua_State *L)
+{
+	GMainContext *context = g_main_context_new ();
+	lglib_main_context_bless (L, context);
+	g_main_context_unref (context);
+	return 1;
+}
+
+/// g.main_context.bless
+/// Blesses given pointer to g main context object.
+/// A: lightuserdata (C g main context object)
+/// R: g main context object
+static int lglib_main_context_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "g main context lightuserdata expected");
+	lglib_main_context_bless (L, lua_touserdata (L, 1));
+	return 1;
+}
+
+/// g.main_context.default
+/// Returns default main context object.
+/// R: g main context object
+static int lglib_main_context_default (lua_State *L)
+{
+	GMainContext *context = g_main_context_default ();
+	lglib_main_context_bless (L, context);
+	// XXX g_main_context_unref (context);
+	return 1;
+}
+
+/// main_context:iteration
+/// Performs single full iteration in blocking or non-blocking manner,
+/// depending on value of argument.
+/// Returns true, if some events have been handled.
+/// A: boolean (optional)
+/// R: boolean
+static int lglib_main_context_iteration (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	int may_block = 0;
+	if ( lua_gettop (L) > 1 ) {
+		luaL_checktype (L, 2, LUA_TBOOLEAN);
+		may_block = lua_toboolean (L, 2);
+	}
+	lua_pushboolean (L, g_main_context_iteration (object->main_context, may_block));
+	return 1;
+}
+
+/// main_context:pending
+/// Indicates, if there are present events in sources.
+/// R: boolean
+static int lglib_main_context_pending (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	lua_pushboolean (L, g_main_context_pending (object->main_context));
+	return 1;
+}
+
+/// main_context:find_source
+/// Returns source with given id.
+/// A: integer (source id)
+/// R: g main context object or nil
+static int lglib_main_context_find_source (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	int id = luaL_checkint (L, 2);
+	GSource *source = g_main_context_find_source_by_id (object->main_context, id);
+	if (source) {
+		lglib_source_bless (L, source);
+		// XXX g_source_unref (source)
+	} else
+		lua_pushnil (L);
+	return 1;
+}
+
+/// main_context:wakeup
+/// Awakes main context if it currently poll() ing.
+/// XXX Seems to be threading-related routine, how lua will work in this case?
+static int lglib_main_context_wakeup (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	g_main_context_wakeup (object->main_context);
+	return 0;
+}
+
+/// main_context:acquire
+/// Tries to own main context.
+/// R: boolean (success)
+static int lglib_main_context_acquire (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	lua_pushboolean (L, g_main_context_acquire (object->main_context));
+	return 1;
+}
+
+/// main_context:release
+/// Disowns acquired context.
+/// Note: release context as many times, as you acquired it.
+static int lglib_main_context_release (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	g_main_context_release (object->main_context);
+	return 0;
+}
+
+/// main_context:owner
+/// Indicates if current thread is owner of context.
+/// R: boolean
+static int lglib_main_context_owner (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	lua_pushboolean (L, g_main_context_is_owner (object->main_context));
+	return 1;
+}
+
+/// main_context:wait
+/// For now this will not be implemented, as it requires threading part of glib
+
+/// WARNING: conventions for next four methods are likely to change
+/// as I become more familiar with all that stuff.
+
+/// main_context:prepare
+/// Polling preparation step.
+/// R: boolean (indicates readiness for dispatching before polling), integer (priority of most important ready source, if first value is true)
+static int lglib_main_context_prepare (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	gint priority;
+	gboolean ready = g_main_context_prepare (object->main_context, &priority);
+	lua_pushboolean (L, ready);
+	if (!ready)
+		return 1;
+	lua_pushnumber (L, priority);
+	return 0;
+}
+
+/// main_context:query
+/// Get necessary for polling information.
+/// A: integer (maximum priority), integer (number of fds to allocate)
+/// R: userdata (GPollFDs), integer (number of fds required), integer (timeout)
+static int lglib_main_context_query (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	int priority = luaL_checkint (L, 2);
+	int nfds = luaL_checkint (L, 3);
+	gint timeout;
+	GPollFD *fds = lua_newuserdata (L, sizeof (GPollFD) * nfds);
+	lua_pushnumber (L, g_main_context_query (object->main_context, priority, &timeout, fds, nfds));
+	lua_pushnumber (L, timeout);
+	return 3;
+}
+
+/// main_context:check
+/// Passes results of polling back to main loop.
+/// A: integer (priority), userdata (GPollFDs), integer (number of fds)
+/// R: boolean (readiness for dispatching)
+static int lglib_main_context_check (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	int priority = luaL_checkint (L, 2);
+	GPollFD *fds = lua_touserdata (L, 3); // FIXME
+	int nfds = luaL_checkint (L, 4);
+	lua_pushboolean (L, g_main_context_check (object->main_context, priority, fds, nfds));
+	return 1;
+}
+
+/// main_context:dispatch
+/// Actually dispatches pending event sources
+static int lglib_main_context_dispatch (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	g_main_context_dispatch (object->main_context);
+	return 0;
+}
+
+/// main_context:poll_function
+/// main_context:add_poll
+/// main_context:remove_poll
+/// Let's delay implementing this until fd objects will be ready...
+
+/// main_context:pointer
+/// Returns pointe to underlying C structure.
+/// A: g main context object
+/// R: lightuserdata
+static int lglib_main_context_pointer (lua_State *L)
+{
+	lglib_main_context_t *object = luaL_checklglib_main_context (L, 1);
+	lua_pushlightuserdata (L, object->main_context);
+	return 1;
+}
+
+static int lglib_main_context_gc (lua_State *L)
+{
+	lglib_main_context_t *object = lua_touserdata (L, 1);
+	g_main_context_unref (object->main_context);
+	return 0;
+}
+
+static const luaL_Reg lglib_main_context_reg_f[] = {
+	{ "new",     lglib_main_context_new       },
+	{ "bless",   lglib_main_context_bless_lua },
+	{ "default", lglib_main_context_default   },
+	{ NULL,      NULL                         },
+};
+
+static const luaL_Reg lglib_main_context_reg_m[] = {
+	{ "iteration",   lglib_main_context_iteration   },
+	{ "pending",     lglib_main_context_pending     },
+	{ "find_source", lglib_main_context_find_source },
+	{ "wakeup",      lglib_main_context_wakeup      },
+	{ "acquire",     lglib_main_context_acquire     },
+	{ "release",     lglib_main_context_release     },
+	{ "owner",       lglib_main_context_owner       },
+	{ "prepare",     lglib_main_context_prepare     },
+	{ "query",       lglib_main_context_query       },
+	{ "check",       lglib_main_context_check       },
+	{ "dispatch",    lglib_main_context_dispatch    },
+	{ "pointer",     lglib_main_context_pointer     },
+	{ "__gc",        lglib_main_context_gc          },
+	{ NULL,          NULL                           },
+};
+
+int luaopen_glib_main_context (lua_State *L)
+{
+	luaL_newmetatable (L, "glib.main_context" );
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, lglib_main_context_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "g.main_context", lglib_main_context_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_main_context.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,10 @@
+
+#ifndef LGLIB_MAIN_CONTEXT
+#define LGLIB_MAIN_CONTEXT
+
+#include <lua.h>
+
+int luaopen_glib_main_context (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_source.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,133 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+
+#include "glib_types.h"
+#include "util.h"
+
+/// g.source.bless
+/// Blesses given pointer to g source object.
+/// A: lightuserdata (pointer to C glib event source object)
+/// R: g source object
+static int lglib_source_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "glib source lightuserdata expected");
+	lglib_source_bless (L, lua_touserdata (L, 1));
+	return 1;
+}
+
+/// source:context
+/// Connects event source to main context or returns context,
+/// to which this source is connected.
+/// A: lglib main context object
+/// R: int (source id, when called with args) or g main context object or nil (if called with no args)
+static int lglib_source_context (lua_State *L)
+{
+	lglib_source_t *object = luaL_checklglib_source (L, 1);
+	if (lua_gettop (L) > 1) { // Attach
+		lglib_main_context_t *context = luaL_checklglib_main_context (L, 2);
+		lua_pushnumber (L, g_source_attach (object->source, context->main_context));
+	} else { // Get
+		GMainContext *context = g_source_get_context (object->source);
+		if (context) {
+			lglib_main_context_bless (L, context);
+			// XXX g_main_context_unref (context);
+		} else
+			lua_pushnil (L);
+	}
+	return 1;
+}
+
+/// source callback function
+/// Does processing of events. Return value indicates, if source should not be destroyed.
+/// R: boolean (continue)
+static gboolean lglib_source_callback (lglib_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	if (lua_pcall (cb->L, 0, 1, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return FALSE;
+	}
+	return lua_toboolean (cb->L, -1);
+}
+
+/// source:callback
+/// Sets callback function for given event source.
+/// A: source callback function
+static int lglib_source_set_callback (lua_State *L)
+{
+	lglib_source_t *object = luaL_checklglib_source (L, 1);
+	lglib_callback_t *cb;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+	
+	cb = luaL_malloc (L, sizeof (lglib_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	g_source_set_callback (object->source, (GSourceFunc)lglib_source_callback,
+			       cb, (GDestroyNotify)lglib_callback_destroy);
+	return 0;
+}
+
+/// source:priority
+/// Sets or gets priority of source.
+/// A: integer (optional priority)
+/// R: integer (when called with no args)
+static int lglib_source_priority (lua_State *L)
+{
+	lglib_source_t *object = luaL_checklglib_source (L, 1);
+	if (lua_gettop (L) > 1) {
+		g_source_set_priority (object->source, luaL_checkint (L, 2));
+		return 0;
+	} else {
+		lua_pushnumber (L, g_source_get_priority (object->source));
+		return 1;
+	}
+}
+
+/// source:pointer
+/// Returns pointer to underlying C structure.
+/// A: g source object
+/// R: lightuserdata
+static int lglib_source_pointer (lua_State *L)
+{
+	lglib_source_t *object = luaL_checklglib_source (L, 1);
+	lua_pushlightuserdata (L, object->source);
+	return 1;
+}
+
+static int lglib_source_gc (lua_State *L)
+{
+	lglib_source_t *object = lua_touserdata (L, 1);
+	g_source_unref (object->source);
+	return 0;
+}
+
+const luaL_Reg lglib_source_reg_f[] = {
+	{ "bless", lglib_source_bless_lua },
+	{ NULL,    NULL                   },
+};
+
+const luaL_Reg lglib_source_reg_m[] = {
+	{ "context",  lglib_source_context      },
+	{ "priority", lglib_source_priority     },
+	{ "callback", lglib_source_set_callback },
+	{ "pointer",  lglib_source_pointer      },
+	{ "__gc",     lglib_source_gc           },
+	{ NULL,       NULL                      },
+};
+
+int luaopen_glib_source (lua_State *L)
+{
+	luaL_newmetatable (L, "glib.source");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, lglib_source_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "g.source", lglib_source_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_source.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,10 @@
+
+#ifndef LGLIB_SOURCE_H
+#define LGLIB_SOURCE_H
+
+#include <lua.h>
+
+int luaopen_glib_source (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_timeout.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,69 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+
+#include "util.h"
+#include "glib_types.h"
+
+/// timeout callback function
+/// Function, that will be called periodically until it returns false.
+/// R: boolean
+static gboolean lglib_timeout_callback (lglib_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	if (lua_pcall (cb->L, 0, 1, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return FALSE;
+	}
+	return lua_toboolean (cb->L, -1);
+}
+
+/// g.timeout.new
+/// Creates new timeout in default context.
+/// A: integer (priority), integer (interval, seconds), timeout callback function
+/// R: integer (id of the event source)
+static int lglib_timeout_new (lua_State *L)
+{
+	int priority = luaL_checkint (L, 1);
+	int interval = luaL_checkint (L, 2);
+	lglib_callback_t *cb;
+	luaL_argcheck (L, lua_isfunction (L, 3), 3, "function expected");
+
+	cb = luaL_malloc (L, sizeof (lglib_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	lua_pushnumber (L, g_timeout_add_seconds_full (priority, interval,
+						       (GSourceFunc)lglib_timeout_callback,
+						       cb,
+						       (GDestroyNotify)lglib_callback_destroy));
+	return 1;
+}
+
+/// g.timeout.source
+/// Creates new timeout source.
+/// A: interval
+/// R: g source object
+static int lglib_timeout_source (lua_State *L)
+{
+	int interval = luaL_checkint (L, 1);
+	GSource *source = g_timeout_source_new_seconds (interval);
+	lglib_source_bless (L, source);
+	// XXX s_source_unref (source);
+	return 1;
+}
+
+static const luaL_Reg lglib_timeout_reg_f[] = {
+	{ "new",    lglib_timeout_new    },
+	{ "source", lglib_timeout_source },
+	{ NULL,     NULL                 },
+};
+
+int luaopen_glib_timeout (lua_State *L)
+{
+	luaL_register (L, "g.timeout", lglib_timeout_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_timeout.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,10 @@
+
+#ifndef LGLIB_TIMEOUT_H
+#define LGLIB_TIMEOUT_H
+
+#include <lua.h>
+
+int luaopen_glib_timeout (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_types.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,58 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+
+#include "glib_types.h"
+#include "util.h"
+
+void lglib_callback_destroy (lglib_callback_t *cb)
+{
+	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	luaL_free (cb->L, cb);
+}
+
+#define LGLIB_CHECK(WHAT, TYPE)                                               \
+lglib_##WHAT##_t *luaL_checklglib_##WHAT (lua_State *L, int index)            \
+{                                                                             \
+	lglib_##WHAT##_t *object = luaL_checkudata (L, index, "glib." #WHAT); \
+	luaL_argcheck (L, object != NULL, index, "glib " #WHAT " expected");  \
+	return object;                                                        \
+}
+
+#define LGLIB_BLESS(WHAT, TYPE)                                                   \
+lglib_##WHAT##_t *lglib_##WHAT##_bless (lua_State *L, TYPE *WHAT)                 \
+{                                                                                 \
+	lglib_##WHAT##_t *object;                              /* top of stack */ \
+	lua_pushstring (L, LGLIB_OBJREGISTRY);       /* 1 registry table name  */ \
+	lua_rawget (L, LUA_REGISTRYINDEX);                 /* 1 registry table */ \
+	lua_pushlightuserdata (L, WHAT);                   /* 2 light userdata */ \
+	lua_rawget (L, -2);                                    /* 2 object/nil */ \
+	if (!lua_isnil (L, -1)) {                                  /* 2 object */ \
+		lua_remove (L, -2);                                /* 1 object */ \
+		object = lua_touserdata (L, -1);                                  \
+		return object;                                                    \
+	}                                                                         \
+	                                                              /* 2 nil */ \
+	lua_remove (L, -1);                                /* 1 registry table */ \
+	object = lua_newuserdata (L, sizeof (lglib_##WHAT##_t)); /* 2 userdata */ \
+	luaL_getmetatable (L, "glib." #WHAT);                   /* 3 metatable */ \
+	lua_setmetatable (L, -2);                                  /* 2 object */ \
+	lua_pushlightuserdata (L, WHAT);                   /* 3 light userdata */ \
+	lua_pushvalue (L, -2);                                     /* 4 object */ \
+	lua_rawset (L, -4);                                        /* 2 object */ \
+	lua_remove (L, -2);                                        /* 1 object */ \
+	object->WHAT = WHAT;                                                      \
+	g_##WHAT##_ref (WHAT);                                                    \
+	return object;                                                            \
+}
+
+#define LGLIB_DEFINE(WHAT, TYPE) \
+LGLIB_CHECK (WHAT, TYPE)         \
+LGLIB_BLESS (WHAT, TYPE)
+
+LGLIB_DEFINE (main_context, GMainContext)
+LGLIB_DEFINE (source, GSource)
+
+#undef LGLIB_DEFINE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib_types.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,34 @@
+
+#ifndef LGLIB_TYPES_H
+#define LGLIB_TYPES_H
+
+#include <lua.h>
+#include <glib.h>
+
+#ifndef LGLIB_OBJREGISTRY
+#define LGLIB_OBJREGISTRY ( "lglib.obj_registry" )
+#endif
+
+typedef struct {
+	int reference;
+	lua_State *L;
+} lglib_callback_t;
+
+void lglib_callback_destroy (lglib_callback_t *cb);
+
+
+#define LGLIB_DECLARE(WHAT, TYPE)                         \
+typedef struct {                                            \
+	TYPE *WHAT;                                           \
+} lglib_##WHAT##_t;                                             \
+                                                                  \
+lglib_##WHAT##_t *luaL_checklglib_##WHAT (lua_State *L, int index); \
+lglib_##WHAT##_t *lglib_##WHAT##_bless (lua_State *L, TYPE *WHAT);
+
+LGLIB_DECLARE (main_context, GMainContext)
+LGLIB_DECLARE (source, GSource)
+
+#undef LGLIB_DECLARE
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,37 @@
+
+#include <lua.h>
+
+#include "lm_types.h"
+#include "lm_message_node.h"
+#include "lm_message.h"
+#include "lm_message_handler.h"
+#include "lm_proxy.h"
+#include "lm_ssl.h"
+#include "lm_connection.h"
+
+int luaopen_loudmouth (lua_State *L)
+{
+	lua_pushstring (L, LLM_OBJREGISTRY); // 1 registry key
+	lua_newtable (L);                    // 2 registry value (table)
+	lua_createtable (L, 0, 1);           // 3 metatable
+	lua_pushstring (L, "__mode");        // 4 metatable key
+	lua_pushstring (L, "v");             // 5 metatable value
+	lua_settable (L, -3);                // 3 metatable
+	lua_setmetatable (L, -2);            // 2 registry value
+	lua_rawset (L, LUA_REGISTRYINDEX);   // 0
+	
+	lua_createtable (L, 6, 0);
+	lua_pushvalue (L, -1);
+	lua_setglobal (L, "lm");
+
+	luaopen_lm_message_node (L);
+	luaopen_lm_message (L);
+	luaopen_lm_message_handler (L);
+	luaopen_lm_proxy (L);
+	luaopen_lm_ssl (L);
+	luaopen_lm_connection (L);
+	lua_pop (L, 6);
+
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm.lua	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,298 @@
+
+require ( 'loudmouth' )
+
+-- argument is a table with keys,
+-- corresponding to method names.
+function lm.proxy.create ( a )
+	if type ( a ) ~= "table" then
+		error ( "arguments should be in a table" )
+	end
+	local p = lm.proxy.new ()
+	if a.server then
+		p:server ( a.server )
+	end
+	if a.port then
+		p:port ( a.port )
+	end
+	if a['type'] then
+		p:type ( a['type'] )
+	end
+	if a.username then
+		p:username ( a.username )
+	end
+	if a.password then
+		p:password ( a.password )
+	end
+	return p
+end
+
+-- argument is a table with two keys:
+-- callback and fingerprint
+function lm.ssl.create ( a )
+	if not lm.ssl.supported () then
+		-- XXX
+		-- error ( "ssl is not supported by your loudmouth library" )
+		return nil
+	end
+	local fp, cb
+	local st = type ( a )
+	if st == "table" then
+		fp = a.fingerprint
+		cb = a.callback
+	elseif st == "function" then
+		cb = a
+	elseif st == "string" then
+		fp = a
+	elseif st ~= "nil" then
+		error ( "unexpected type of argument" )
+	end
+	if fp then
+		if cb then
+			return lm.ssl.new ( fp, cb )
+		else
+			return lm.ssl.new ( fp )
+		end
+	else
+		if cb then
+			return lm.ssl.new ( cb )
+		else
+			return lm.ssl.new ()
+		end
+	end
+end
+
+-- basically, it just provides a way
+-- to initialize many parameters at once.
+-- keys in a table correspond to methods
+-- of connection object, except for handlers,
+-- where format is {
+--     "type/priority" = function/object,
+--     ...
+-- }
+-- two extra keys - server and context.
+-- ssl and proxy objects can either be objects
+-- or tables, directly passed to corresponding
+-- create routine.
+function lm.connection.create ( a )
+	local at = type (a)
+	if at == "string" then
+		return lm.connection.new ( a )
+	elseif at == "table" then
+		local server = a.server
+		if not server then
+			error ( "server name parameter required" )
+		end
+
+		-- create connection object
+		local c
+		if a.context then
+			c = lm.connection.new ( server, a.context )
+		else
+			c = lm.connection.new ( server )
+		end
+
+		-- connection parameters
+		if a.port then
+			c:port ( a.port )
+		end
+		if a.jid then
+			c:jid ( a.jid )
+		end
+		if a.keep_alive_rate then
+			c:keep_alive_rate ( a.keep_alive_rate )
+		end
+
+		-- proxy
+		if a.proxy then
+			local pt = type ( a.proxy )
+			if pt == "userdata" then
+				c:proxy ( a.proxy )
+			else
+				local proxy = lm.proxy.create ( a.proxy )
+				c:proxy ( proxy )
+			end
+		end
+
+		-- ssl
+		if a.ssl then
+			local st = type ( a.ssl )
+			if st == "userdata" then
+				c:ssl ( a.ssl )
+			else
+				local ssl = lm.ssl.create ( a.ssl )
+				c:ssl ( ssl )
+			end
+		end
+
+		-- disconnect callback
+		if a.ondisconnect then
+			c:ondisconnect ( a.ondisconnect )
+		end
+
+		-- message handlers
+		if a.handlers then
+			if type ( a.handlers ) ~= "table" then
+				error ( "handlers parameter expected to be a table " ..
+				        "of the form { \"type/priority\" = function/object }" )
+			end
+			for mhtype, handler in pairs ( a.handlers ) do
+				local mtype, prio = mhtype:match ( "(.-)/(%d+)" )
+				if not mtype then
+					mtype = mhtype
+					prio = 0
+				else
+					prio = tonumber ( prio )
+				end
+				c:handler ( handler, mtype, prio )
+			end
+		end
+
+		return c
+	else
+		error ( "at least server name parameter required" )
+	end
+end
+
+-- recursively fills a node, see lm.message.create
+function lm.message_node.fill ( n, a )
+	for name, value in pairs ( a ) do
+		if type ( value ) == "table" then
+			if type ( value[1] ) == "table" then
+				for index, instance in ipairs ( value ) do
+					lm.message_node.fill ( n:child ( name, "" ), instance )
+				end
+			else
+				lm.message_node.fill ( n:child ( name, "" ), value )
+			end
+		elseif name == 1 then
+			n:value ( value )
+		else
+			n:attribute ( name, value )
+		end
+	end
+end
+
+--[[
+-- recursively fills a message
+lm.message.create { mtype = 'iq-result', to = 'foo@bar.xyz',
+	command = { xmlns = 'http://jabber.org/protocol/commands', node = 'http://jabber.org/protocol/rc#set-status', status = 'executing', sessionid = 'set-status:aaa3',
+		x = { xmlns = 'jabber:x:data', type = 'form',
+			title = { "Change Status" },
+			instructions = { "Choose the status and status message" },
+			field = {{ type = 'hidden', var = 'FORM_TYPE',
+				value = { "http://jabber.org/protocol/rc" },
+			},{ type = 'list-single', label = 'Status', var = 'status',
+				required = { },
+				value = { "online" },
+				option = {{ label = 'Chat',
+					value = { "chat" },
+				},{ label = 'Online',
+					value = { "online" },
+				},{ label = 'Away',
+					value = { "away" },
+				},{ label = 'Extended Away',
+					value = { "xa" },
+				},{ label = 'Do Not Disturb',
+					value = { "dnd" },
+				},{ label = 'Invisible',
+					value = { "invisible" },
+				},{ label = 'Offline',
+					value = { "offline" },
+				}},
+			},{ type = 'text-single', label = 'Priority', var = 'status-priority',
+				value = { "5" },
+			},{ type = 'text-multi', label = 'Message', var = 'status-message' }},
+		},
+	},
+}
+--]]
+function lm.message.create ( a )
+	if type ( a ) ~= "table" then
+		error ( "table expected as argument" )
+	end
+	if not a.mtype or not a.to then
+		error ( "you must specify message type and destination" )
+	end
+	local mtype, subtype = a.mtype:match ( "(.-)%-(.+)" )
+	local m
+	if not mtype then
+		m = lm.message.new ( a.to, a.mtype )
+	else
+		m = lm.message.new ( a.to, mtype, subtype )
+	end
+	a.to = nil
+	a.mtype = nil
+	lm.message_node.fill ( m:node(), a )
+	return m
+end
+
+-- TODO: multiple nodes with same name
+function lm.message_node.parse ( node, r )
+	local n = node:children ()
+	while n do
+		local name = n:name ()
+		r[name] = { }
+		local value = n:value ()
+		if value then
+			r[name][1] = value
+		end
+		lm.message_node.parse ( n, r[name] )
+		n = n:next ()
+	end
+end
+
+-- There are NO WAY to get a list of node attributes,
+-- except brute force...
+function lm.message.parse ( message )
+	local node = message:node ()
+	local mtype, subtype = message:type ()
+	if subtype then
+		mtype = mtype .. '-' .. subtype
+	end
+	local r = { mtype = mtype }
+	local value = node:value ()
+	if value then
+		r[1] = value
+	end
+	lm.message_node.parse ( node, r )
+	return r
+end
+
+-- the same table, as for lm.connection.create, but with few more fields:
+-- ssl.validate
+-- onconnect
+-- onopen
+-- username
+-- password
+-- resource
+function lm.connect ( a )
+	if type ( a ) ~= "table" then
+		error ( "table expected as argument" )
+	end
+	if a.ssl then
+		if a.ssl.validate and not a.ssl.callback then
+			a.ssl.callback =
+				function ( obj, status )
+					return false
+				end
+		end
+	end
+	local c = lm.connection.create ( a )
+	c:open (
+		function ( obj, status )
+			if status then
+				if type ( a.onopen ) == "function" then
+					a.onopen ( obj )
+				end
+				c:authenticate ( a.username, a.password, a.resource,
+					function ( obj, status )
+						if type ( a.onconnect ) == "function" then
+							a.onconnect ( obj )
+						end
+					end )
+			end
+		end )
+	return c
+end
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_connection.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,542 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>			// GDestroyNotify, GMainContext
+#include <loudmouth/loudmouth.h>
+
+#include "util.h"
+#include "lm_types.h"
+#include "lm_message.h"
+#include "lm_message_handler.h"
+
+/// lm.connection
+/// Central module, representing connection to the server.
+/// You should create a new connection object, then open it (establish
+/// connection), then authenticate to the server.
+
+/// connection state
+/// Stirng, representing current connection state.
+/// V: closed, opening, open, authenticating, authenticated
+const string2enum_t llm_connection_state[] = {
+	{ "closed",         LM_CONNECTION_STATE_CLOSED         },
+	{ "opening",        LM_CONNECTION_STATE_OPENING        },
+	{ "open",           LM_CONNECTION_STATE_OPEN           },
+	{ "authenticating", LM_CONNECTION_STATE_AUTHENTICATING },
+	{ "authenticated",  LM_CONNECTION_STATE_AUTHENTICATED  },
+	{ NULL,             0                                  }, // XXX
+};
+
+/// handler priority
+/// String, according to which handler will be placed into one
+/// of three handler groups.
+/// V: last, normal, first
+const string2enum_t llm_handler_priority[] = {
+	{ "last",   LM_HANDLER_PRIORITY_LAST   },
+	{ "normal", LM_HANDLER_PRIORITY_NORMAL },
+	{ "first",  LM_HANDLER_PRIORITY_FIRST  },
+	{ NULL,     0                          }, // XXX
+};
+
+/// disconnect reason
+/// String, indicating the reason of disconnection occured.
+/// V: ok, ping time out, hup, error, resource conflict, invalid xml, unknown, ping_time_out, resource_conflict, invalid_xml
+const string2enum_t llm_disconnect_reason[] = {
+	{ "ok",                LM_DISCONNECT_REASON_OK                },
+	{ "ping time out",     LM_DISCONNECT_REASON_PING_TIME_OUT     },
+	{ "hup",               LM_DISCONNECT_REASON_HUP               },
+	{ "error",             LM_DISCONNECT_REASON_ERROR             },
+	{ "resource conflict", LM_DISCONNECT_REASON_RESOURCE_CONFLICT },
+	{ "invalid xml",       LM_DISCONNECT_REASON_INVALID_XML       },
+	{ "unknown",           LM_DISCONNECT_REASON_UNKNOWN           },
+	{ "ping_time_out",     LM_DISCONNECT_REASON_PING_TIME_OUT     },
+	{ "resource_conflict", LM_DISCONNECT_REASON_RESOURCE_CONFLICT },
+	{ "invalid_xml",       LM_DISCONNECT_REASON_INVALID_XML       },
+	{ NULL,                0                                      }, // XXX
+};
+
+/// lm.connection.new
+/// Creates a new connection (closed).
+/// A: string (server name), lightuserdata (C glib main context object, optional)
+/// R: lm connection object
+static int llm_connection_new (lua_State *L)
+{
+	const char *server = luaL_checkstring (L, 1);
+	LmConnection *connection;
+	if (lua_gettop (L) < 2) {
+		connection = lm_connection_new (server);
+		lua_pop (L, 1);
+	} else {
+		luaL_argcheck (L, lua_islightuserdata (L, 2), 2, "glib main context lightuserdata expected");
+		connection = lm_connection_new_with_context (server, (GMainContext *) lua_touserdata (L, 2));
+		lua_pop (L, 2);
+	}
+	llm_connection_bless (L, connection);
+	lm_connection_unref (connection);
+	return 1;
+}
+
+/// lm.connection.bless
+/// Blesses given pointer to lm connection object.
+/// Note: it adds a reference to connection.
+/// A: lightuserdata (C lm connection object)
+/// R: lm connection object
+static int llm_connection_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "loudmouth connection lightuserdata expected");
+	llm_connection_bless (L, (LmConnection *) lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// connection callback function
+/// User function, that will be called on connection establishment operation end,
+/// eg. successful/unsuccessful opening or authentication.
+/// A: lm connection object, boolean (success)
+static void llm_connection_callback (LmConnection *connection, int success, llm_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	llm_connection_bless (cb->L, connection);
+	// XXX lm_connection_unref (connection);
+	lua_pushboolean (cb->L, success);
+	if (lua_pcall (cb->L, 2, 0, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return;
+	}
+}
+
+/// connection:open
+/// Opens connection to the server and then calls callback function.
+/// A: connection callback function
+/// R: boolean (success)
+static int llm_connection_open (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	llm_callback_t *cb;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+	
+	cb = luaL_malloc (L, sizeof (llm_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	lua_pushboolean (L, lm_connection_open (object->connection,
+						(LmResultFunction)llm_connection_callback, cb,
+						(GDestroyNotify)llm_callback_destroy, NULL));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// connection:authenticate
+/// Tries to authenticate against opened connection, then calls callback function.
+/// A: string (username), string (password), string (resource), connection callback function
+/// R: boolean (success)
+static int llm_connection_authenticate (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	const char *username = luaL_checkstring (L, 2);
+	const char *password = luaL_checkstring (L, 3);
+	const char *resource = luaL_checkstring (L, 4);
+	llm_callback_t *cb;
+	int status;
+	luaL_argcheck (L, lua_isfunction (L, 5), 5, "function expected");
+
+	cb = luaL_malloc (L, sizeof (llm_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	status = lm_connection_authenticate (object->connection, username, password, resource,
+						(LmResultFunction)llm_connection_callback, cb,
+						(GDestroyNotify)llm_callback_destroy, NULL);
+	lua_pop (L, 4);
+	lua_pushboolean (L, status);
+	return 1;
+}
+
+/// connection:port
+/// Gets or sets server port to connect.
+/// A: integer (optional)
+/// R: integer (when called with no args)
+static int llm_connection_port (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_connection_set_port (object->connection, luaL_checkint (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushnumber (L, lm_connection_get_port (object->connection));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// connection:server
+/// Gets or sets server to connect to.
+/// A: string (optional, server name)
+/// R: string (when called with no args)
+static int llm_connection_server (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_connection_set_server (object->connection, luaL_checkstring (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_connection_get_server (object->connection));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// connection:jid
+/// Gets or sets jid for connection.
+/// A: string (optional)
+/// R: string (when called with no args)
+static int llm_connection_jid (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_connection_set_jid (object->connection, luaL_checkstring (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_connection_get_jid (object->connection));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// connection:keep_alive_rate
+/// Gets or sets keep alive packets rate for connection.
+/// Note, that on some platforms there is no get function even in
+/// loudmouth versions, that should have it according to documentation.
+/// integer (optional, seconds)
+/// integer (when called with no args)
+static int llm_connection_keep_alive_rate (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_connection_set_keep_alive_rate (object->connection, luaL_checkint (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+#ifdef HAVE_LM_CONNECTION_GET_KEEP_ALIVE_RATE
+		lua_pushnumber (L, lm_connection_get_keep_alive_rate (object->connection));
+		lua_remove (L, -2);
+#else
+		lua_pop (L, 1);
+		lua_pushstring (L, "Sorry, your loudmouth have no get_keep_alive_rate");
+		lua_error (L);
+#endif
+		return 1;
+	}
+}
+
+/// connection:proxy
+/// Gets or sets proxy server for connection.
+/// A: lm proxy object (optional)
+/// R: lm proxy object or nil (when called with no args)
+static int llm_connection_proxy (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		llm_proxy_t *proxy = luaL_checklm_proxy (L, 2);
+		lm_connection_set_proxy (object->connection, proxy->proxy);
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		LmProxy *proxy = lm_connection_get_proxy (object->connection);
+		lua_pop (L, 1);
+		if (proxy) {
+			llm_proxy_bless (L, proxy);
+			// XXX lm_proxy_unref (proxy);
+		} else
+			lua_pushnil (L);
+		return 1;
+	}
+}
+
+/// connection:ssl
+/// Gets or sets ssl object for connection.
+/// A: lm ssl object (optional)
+/// R: lm ssl object or nil (when called with no args)
+static int llm_connection_ssl (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		llm_ssl_t *ssl = luaL_checklm_ssl (L, 2);
+		lm_connection_set_ssl (object->connection, ssl->ssl);
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		LmSSL *ssl = lm_connection_get_ssl (object->connection);
+		lua_pop (L, 1);
+		if (ssl) {
+			llm_ssl_bless (L, ssl);
+			// XXX lm_ssl_unref (ssl);
+		} else
+			lua_pushnil (L);
+		return 1;
+	}
+}
+
+/// connection:close
+/// Close connection.
+/// R: boolean (success)
+static int llm_connection_close (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	lua_pushboolean (L, lm_connection_close (object->connection, NULL));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// connection:status
+/// Returns string, describing connection state.
+/// R: connection state
+static int llm_connection_status (lua_State *L)
+{
+	llm_connection_t *connection = luaL_checklm_connection (L, 1);
+	luaL_pushenum (L, lm_connection_get_state (connection->connection), llm_connection_state)
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// connection:send
+/// Sends message (object). If specified, handler function will be called upon
+/// receiving of response to that message.
+/// Handler function can be either message handler object or just a function.
+/// A: lm message object, message handler callback function or lm message handler object (optional)
+/// R: boolean (success)
+static int llm_connection_send (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	llm_message_t *message = luaL_checklm_message (L, 2);
+	int status;
+	if (lua_gettop (L) < 3) // Send
+		status = lm_connection_send (object->connection, message->message, NULL);
+	else if (lua_isfunction (L, 3)) { // Send w/reply, func
+		llm_callback_t *cb = luaL_malloc (L, sizeof (llm_callback_t));
+		LmMessageHandler *handler;
+
+		cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+		cb->L = L;
+		handler = lm_message_handler_new (
+					(LmHandleMessageFunction)llm_message_handler_callback,
+					cb, (GDestroyNotify)llm_callback_destroy);
+		status = lm_connection_send_with_reply (object->connection,
+							message->message, handler, NULL);
+		lm_message_handler_unref (handler);
+	} else { // Send w/reply, handler
+		llm_message_handler_t *handler = luaL_checklm_message_handler (L, 3);
+		status = lm_connection_send_with_reply (object->connection, message->message,
+							handler->message_handler, NULL);
+		lua_pop (L, 1);
+	};
+	lua_pop (L, 2);
+	lua_pushboolean (L, status);
+	return 1;
+}
+
+/// connection:send_raw
+/// Sends arbitrary string to opened connection.
+/// A: string
+/// R: boolean (status)
+static int llm_connection_send_raw (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	const char *string = luaL_checkstring (L, 2);
+	int status = lm_connection_send_raw (object->connection, string, NULL);
+	lua_pop (L, 2);
+	lua_pushboolean (L, status);
+	return 1;
+}
+
+/// connection:handler
+/// Registers or unregisters handler function for a given type of messages.
+/// To unregister handler, omit the priority argument.
+/// Handler function can be specified as plain function or message handler object.
+/// Though, you can unregister only a message handler object.
+/// A: message handler callback function or lm message handler object, message type, handler priority (optional)
+static int llm_connection_handler (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	int type = luaL_checkenum (L, 3, llm_message_type);
+
+	if (lua_gettop (L) > 3) { // Register
+		int priority = luaL_checkenum (L, 4, llm_handler_priority);
+
+		if (lua_isfunction (L, 2)) { // Function
+			LmMessageHandler *handler;
+			llm_callback_t *cb = luaL_malloc (L, sizeof (llm_callback_t));
+			lua_pushvalue (L, 2);
+			cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+			cb->L         = L;
+
+			handler = lm_message_handler_new (
+					(LmHandleMessageFunction)llm_message_handler_callback,
+					cb, (GDestroyNotify)llm_callback_destroy);
+			lm_connection_register_message_handler (object->connection,
+								handler, type, priority);
+			lm_message_handler_unref (handler);
+		} else { // Object
+			llm_message_handler_t *handler = luaL_checklm_message_handler (L, 2);
+			lm_connection_register_message_handler (object->connection,
+								handler->message_handler,
+								type, priority);
+		}
+		lua_pop (L, 1);
+	} else { // Unregister
+		llm_message_handler_t *handler = luaL_checklm_message_handler (L, 2);
+		lm_connection_unregister_message_handler (object->connection,
+							  handler->message_handler,
+							  type);
+	}
+	lua_pop (L, 2);
+	return 0;
+}
+
+/// disconnect callback function
+/// Function, that will be called when disconnection occurs.
+/// A: lm connection object, disconnect reason
+void llm_disconnect_callback (LmConnection *connection, LmDisconnectReason reason, llm_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	llm_connection_bless (cb->L, connection);
+	// XXX lm_connection_unref (connection);
+	luaL_pushenum (cb->L, reason, llm_disconnect_reason);
+	if (lua_pcall (cb->L, 2, 0, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return;
+	}
+}
+
+/// connection:ondisconnect
+/// Sets callback, that will be called on connection disconnect.
+/// A: disconnect callback function
+static int llm_connection_ondisconnect (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	llm_callback_t *cb;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	cb = luaL_malloc (L, sizeof (llm_callback_t));
+	cb->reference  = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L          = L;
+
+	lm_connection_set_disconnect_function (object->connection,
+					       (LmDisconnectFunction)llm_disconnect_callback,
+					       cb, (GDestroyNotify)llm_callback_destroy);
+	lua_pop (L, 1);
+	return 0;
+}
+
+/// connection:open_wait
+/// Synchronous open call, that will block until connection will be opened.
+/// R: boolean (success)
+static int llm_connection_open_wait (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	lua_pushboolean (L, lm_connection_open_and_block (object->connection, NULL));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// connection:authenticate_wait
+/// Synchronous authentication call, that will wait until the end of authentication.
+/// A: string (username), string (password), string (resource)
+/// R: boolean (success)
+static int llm_connection_authenticate_wait (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	const char *username = luaL_checkstring (L, 2);
+	const char *password = luaL_checkstring (L, 3);
+	const char *resource = luaL_checkstring (L, 4);
+	int status = lm_connection_authenticate_and_block (object->connection, username,
+								password, resource, NULL);
+	lua_pop (L, 4);
+	lua_pushboolean (L, status);
+	return 1;
+}
+
+/// connection:send_wait
+/// Synchronous call, that will send message and wait for reply to it.
+/// A: lm message object
+/// R: lm message object or nil
+static int llm_connection_send_wait (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	llm_message_t *message = luaL_checklm_message (L, 2);
+	LmMessage *new = lm_connection_send_with_reply_and_block (object->connection,
+									message->message, NULL);
+	lua_pop (L, 2);
+	if (!new)
+		lua_pushnil (L);
+	else {
+		llm_message_bless (L, new);
+		lm_message_unref (new); // XXX
+	}
+	return 1;
+}
+
+/// connection:pointer
+/// Returns pointer to underlying C loudmouth structure.
+/// R: lightuserdata
+static int llm_connection_pointer (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	lua_pushlightuserdata (L, object->connection);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_connection_gc (lua_State *L)
+{
+	llm_connection_t *object = luaL_checklm_connection (L, 1);
+	lm_connection_unref (object->connection);
+	lua_pop (L, 1);
+	return 0;
+}
+
+static const luaL_Reg llm_connection_reg_f[] = {
+	{ "new",   llm_connection_new       },
+	{ "bless", llm_connection_bless_lua },
+	{ NULL,    NULL                     },
+};
+
+static const luaL_Reg llm_connection_reg_m[] = {
+	{ "open",                llm_connection_open                },
+	{ "close",               llm_connection_close               },
+	{ "authenticate",        llm_connection_authenticate        },
+	{ "port",                llm_connection_port                },
+	{ "server",              llm_connection_server              },
+	{ "jid",                 llm_connection_jid                 },
+	{ "keep_alive_rate",     llm_connection_keep_alive_rate     },
+	{ "state",               llm_connection_status              },
+	{ "proxy",               llm_connection_proxy               },
+	{ "ssl",                 llm_connection_ssl                 },
+	{ "send",                llm_connection_send                },
+	{ "send_raw",            llm_connection_send_raw            },
+	{ "handler",             llm_connection_handler             },
+	{ "ondisconnect",        llm_connection_ondisconnect        },
+	{ "open_wait",           llm_connection_open_wait           },
+	{ "authenticate_wait",   llm_connection_authenticate_wait   },
+	{ "send_wait",           llm_connection_send_wait           },
+	{ "pointer",             llm_connection_pointer             },
+	{ "__gc",                llm_connection_gc                  },
+	{ NULL,                  NULL                               },
+};
+
+int luaopen_lm_connection (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.connection");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_connection_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.connection", llm_connection_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_connection.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,15 @@
+
+#ifndef LLM_CONNECTION_H
+#define LLM_CONNECTION_H
+
+#include <lua.h>
+
+#include "util.h"
+
+extern const string2enum_t llm_connection_state[];
+extern const string2enum_t llm_handler_priority[];
+
+int luaopen_lm_connection (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,163 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <loudmouth/loudmouth.h>
+
+#include "util.h"
+#include "lm_types.h"
+
+/// lm.message
+/// Module, representing individual message.
+/// Message have a type and optionally a sub type.
+
+/// message type
+/// Message type (root tag type).
+/// V: message, presence, iq, stream, stream error, stream features, auth, challenge, response, success, failure, proceed, starttls, unknown, stream:stream, stream:error, stream:feature
+const string2enum_t llm_message_type[] = {
+	{ "message",         LM_MESSAGE_TYPE_MESSAGE         },
+	{ "presence",        LM_MESSAGE_TYPE_PRESENCE        },
+	{ "iq",              LM_MESSAGE_TYPE_IQ              },
+	{ "stream",          LM_MESSAGE_TYPE_STREAM          },
+	{ "stream error",    LM_MESSAGE_TYPE_STREAM_ERROR    },
+	{ "stream features", LM_MESSAGE_TYPE_STREAM_FEATURES },
+	{ "auth",            LM_MESSAGE_TYPE_AUTH            },
+	{ "challenge",       LM_MESSAGE_TYPE_CHALLENGE       },
+	{ "response",        LM_MESSAGE_TYPE_RESPONSE        },
+	{ "success",         LM_MESSAGE_TYPE_SUCCESS         },
+	{ "failure",         LM_MESSAGE_TYPE_FAILURE         },
+	{ "proceed",         LM_MESSAGE_TYPE_PROCEED         },
+	{ "starttls",        LM_MESSAGE_TYPE_STARTTLS        },
+	{ "unknown",         LM_MESSAGE_TYPE_UNKNOWN         },
+	{ "stream:stream",   LM_MESSAGE_TYPE_STREAM          },
+	{ "stream:error",    LM_MESSAGE_TYPE_STREAM_ERROR    },
+	{ "stream:features", LM_MESSAGE_TYPE_STREAM_FEATURES },
+	{ NULL,              0                               },
+};
+
+/// message sub type
+/// Message subtype, not all combinations of type and subtype are possible.
+/// V: not set, available, normal, chat, groupchat, headline, unavailable, probe, subscribe, unsubscribe, subscribed, unsubscribed, get, set, result, error, not_set
+const string2enum_t llm_message_sub_type[] = {
+	{ "not set",       LM_MESSAGE_SUB_TYPE_NOT_SET      },
+	{ "available",     LM_MESSAGE_SUB_TYPE_AVAILABLE    },
+	{ "normal",        LM_MESSAGE_SUB_TYPE_NORMAL       },
+	{ "chat",          LM_MESSAGE_SUB_TYPE_CHAT         },
+	{ "groupchat",     LM_MESSAGE_SUB_TYPE_GROUPCHAT    },
+	{ "headline",      LM_MESSAGE_SUB_TYPE_HEADLINE     },
+	{ "unavailable",   LM_MESSAGE_SUB_TYPE_UNAVAILABLE  },
+	{ "probe",         LM_MESSAGE_SUB_TYPE_PROBE        },
+	{ "subscribe",     LM_MESSAGE_SUB_TYPE_SUBSCRIBE    },
+	{ "unsubscribe",   LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE  },
+	{ "subscribed",    LM_MESSAGE_SUB_TYPE_SUBSCRIBED   },
+	{ "unsubscribed",  LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED },
+	{ "get",           LM_MESSAGE_SUB_TYPE_GET          },
+	{ "set",           LM_MESSAGE_SUB_TYPE_SET          },
+	{ "result",        LM_MESSAGE_SUB_TYPE_RESULT       },
+	{ "error",         LM_MESSAGE_SUB_TYPE_ERROR        },
+	{ "not_set",       LM_MESSAGE_SUB_TYPE_NOT_SET      },
+	{ NULL,            0                                },
+};
+
+/// lm.message.new
+/// Creates new message object.
+/// A: string (to), message type, message sub type (optional)
+/// R: lm message object
+static int llm_message_new (lua_State *L)
+{
+	const char *to   = luaL_checkstring (L, 1);
+	int         type = luaL_checkenum (L, 2, llm_message_type);
+	LmMessage *message;
+	if (lua_gettop (L) > 2) {
+		message = lm_message_new_with_sub_type (to, type,
+						luaL_checkenum (L, 3, llm_message_sub_type));
+		lua_pop (L, 1);
+	} else
+		message = lm_message_new (to, type);
+	lua_pop (L, 2);
+	llm_message_bless (L, message);
+	lm_message_unref (message);
+	return 1;
+}
+
+/// lm.message.bless
+/// Blesses given pointer to lm message object.
+/// A: lightuserdata (C lm message object)
+/// R: lm message object
+static int llm_message_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm message lightuserdata expected");
+	llm_message_bless (L, lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message:node
+/// Returns root node object of message.
+/// R: lm message node object
+static int llm_message_node (lua_State *L)
+{
+	llm_message_t *object = luaL_checklm_message (L, 1);
+	LmMessageNode *node = lm_message_get_node (object->message);
+	llm_message_node_bless (L, node);
+	// XXX lm_message_node_unref (node);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message:type
+/// Returns two strings: message type and message sub type.
+/// R: message type, message sub type
+static int llm_message_kind (lua_State *L)
+{
+	llm_message_t *object = luaL_checklm_message (L, 1);
+	luaL_pushenum (L, lm_message_get_type (object->message), llm_message_type);
+	luaL_pushenum (L, lm_message_get_sub_type (object->message), llm_message_sub_type);
+	lua_remove (L, -3);
+	return 2;
+}
+
+/// message:pointer
+/// Returns pointer to underlying C loudmouth structure.
+/// R: lightuserdata
+static int llm_message_pointer (lua_State *L)
+{
+	llm_message_t *object = luaL_checklm_message (L, 1);
+	lua_pushlightuserdata (L, object->message);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_message_gc (lua_State *L)
+{
+	llm_message_t *object = luaL_checklm_message (L, 1);
+	lm_message_unref (object->message);
+	lua_pop (L, 1);
+	return 0;
+}
+
+static const luaL_Reg llm_message_reg_f[] = {
+	{ "new",   llm_message_new       },
+	{ "bless", llm_message_bless_lua },
+	{ NULL,    NULL                  },
+};
+
+static const luaL_Reg llm_message_reg_m[] = {
+	{ "node",    llm_message_node    },
+	{ "type",    llm_message_kind    },
+	{ "pointer", llm_message_pointer },
+	{ "__gc",    llm_message_gc      },
+	{ NULL,      NULL                },
+};
+
+int luaopen_lm_message (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.message");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_message_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.message", llm_message_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,15 @@
+
+#ifndef LLM_MESSAGE_H
+#define LLM_MESSAGE_H
+
+#include <lua.h>
+
+#include "util.h"
+
+extern const string2enum_t llm_message_type[];
+extern const string2enum_t llm_message_sub_type[];
+
+int luaopen_lm_message (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message_handler.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,138 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+#include <loudmouth/loudmouth.h>
+
+#include "lm_types.h"
+#include "util.h"
+
+/// lm.message_handler
+/// Module, representing message handling functions.
+
+/// message handler callback function
+/// Callback function, used on incoming messages.
+/// Should return true if message has been fully handled and no more
+/// handlers should be called.
+/// A: lm connection object, lm message object
+/// R: boolean
+LmHandlerResult llm_message_handler_callback (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, llm_callback_t *cb)
+{
+	int ret;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	llm_connection_bless (cb->L, connection);
+	// XXX lm_connection_unref (connection);
+	llm_message_bless (cb->L, message);
+	// XXX lm_message_unref (message);
+	if (lua_pcall (cb->L, 2, 1, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	ret = lua_toboolean (cb->L, -1);
+	lua_pop (cb->L, 1);
+	if (ret)
+		return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+	else
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+}
+
+/// lm.message_handler.new
+/// Creates new message handler object.
+/// A: message handler callback function
+/// R: lm message handler object
+static int llm_message_handler_new (lua_State *L)
+{
+	llm_callback_t *cb;
+	LmMessageHandler *handler;
+	luaL_argcheck (L, lua_isfunction (L, 1), 1, "function expected");
+
+	cb = luaL_malloc (L, sizeof (llm_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	handler = lm_message_handler_new ((LmHandleMessageFunction)llm_message_handler_callback,
+					cb, (GDestroyNotify)llm_callback_destroy);
+	llm_message_handler_bless (L, handler);
+	lm_message_handler_unref (handler); // XXX
+	return 1;
+}
+
+/// lm.message_handler.bless
+/// Blesses given pointer to lm message handler object.
+/// A: lightuserdata (C lm message handler object)
+/// R: lm message handler object
+static int llm_message_handler_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "loudmouth message handler pointer expected");
+	llm_message_handler_bless (L, lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_handler:invalidate
+/// Invalidates handler.
+static int llm_message_handler_invalidate (lua_State *L)
+{
+	llm_message_handler_t *object = luaL_checklm_message_handler (L, 1);
+	lm_message_handler_invalidate (object->message_handler);
+	lua_pop (L, 1);
+	return 0;
+}
+
+/// message_handler:valid
+/// Returns true if message handler is still valid.
+/// R: boolean
+static int llm_message_handler_valid (lua_State *L)
+{
+	llm_message_handler_t *object = luaL_checklm_message_handler (L, 1);
+	lua_pushboolean (L, lm_message_handler_is_valid (object->message_handler));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_handler:pointer
+/// Returns pointer to underlying C loudmouth structure.
+/// R: lightuserdata
+static int llm_message_handler_pointer (lua_State *L)
+{
+	llm_message_handler_t *object = luaL_checklm_message_handler (L, 1);
+	lua_pushlightuserdata (L, object->message_handler);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_message_handler_gc (lua_State *L)
+{
+	llm_message_handler_t *object = luaL_checklm_message_handler (L, 1);
+	lm_message_handler_unref (object->message_handler);
+	lua_pop (L, 1);
+	return 0;
+}
+
+static const luaL_Reg llm_message_handler_reg_f[] = {
+	{ "new",   llm_message_handler_new       },
+	{ "bless", llm_message_handler_bless_lua },
+	{ NULL,    NULL                          },
+};
+
+static const luaL_Reg llm_message_handler_reg_m[] = {
+	{ "invalidate", llm_message_handler_invalidate },
+	{ "valid",      llm_message_handler_valid      },
+	{ "pointer",    llm_message_handler_pointer    },
+	{ "__gc",       llm_message_handler_gc         },
+	{ NULL,         NULL                           },
+};
+
+int luaopen_lm_message_handler (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.message_handler");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_message_handler_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.message_handler", llm_message_handler_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message_handler.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,14 @@
+
+#ifndef LLM_MESSAGE_HANDLER_H
+#define LLM_MESSAGE_HANDLER_H
+
+#include <lua.h>
+#include <loudmouth/loudmouth.h>
+
+#include "lm_types.h"
+
+LmHandlerResult llm_message_handler_callback (LmMessageHandler *handler, LmConnection *connection, LmMessage *message, llm_callback_t *cb);
+int luaopen_lm_message_handler (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message_node.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,267 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <loudmouth/loudmouth.h>
+
+#include "lm_types.h"
+
+/// lm.message_node
+/// Object, representing xml node of the message.
+/// Cannot be created directly, only as a part of message tree.
+
+/// lm.message_node.bless
+/// Blesses given pointer to lm message node object.
+/// A: lightuserdata (C lm message node object)
+/// R: lm message node object
+static int llm_message_node_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "loudmouth message node lightuserdata expected");
+	llm_message_node_bless (L, lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:name
+/// Gets a name of message node.
+/// R: string
+static int llm_message_node_name (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	lua_pushstring (L, object->message_node->name);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:next
+/// Gets next message node.
+/// R: lm message node object or nil
+static int llm_message_node_next (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	LmMessageNode *node = object->message_node->next;
+	if (node)
+		llm_message_node_bless (L, node);
+	else
+		lua_pushnil (L);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:prev
+/// Gets previous message node.
+/// R: lm message node object or nil
+static int llm_message_node_prev (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	LmMessageNode *node = object->message_node->prev;
+	if (node)
+		llm_message_node_bless (L, node);
+	else
+		lua_pushnil (L);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:children
+/// Gets first child node (raw access used).
+/// R: lm message node object or nil
+static int llm_message_node_children (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	LmMessageNode *node = object->message_node->children;
+	if (node)
+		llm_message_node_bless (L, node);
+	else
+		lua_pushnil (L);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:parent
+/// Gets parent message node.
+/// R: lm message node object or nil
+static int llm_message_node_parent (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	LmMessageNode *node = object->message_node->parent;
+	if (node)
+		llm_message_node_bless (L, node);
+	else
+		lua_pushnil (L);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:value
+/// Gets or sets a value of message node.
+/// A: string (optional value)
+/// R: string (if called without arguments)
+static int llm_message_node_value (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		const char *value = luaL_checkstring (L, 2);
+		lm_message_node_set_value (object->message_node, value);
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_message_node_get_value (object->message_node));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// message_node:child
+/// Gets or creates child node object with given name.
+/// A: string (name), string (optional value)
+/// R: lm message node object or nil
+static int llm_message_node_child (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	const char *name = luaL_checkstring (L, 2);
+	LmMessageNode *child;
+
+	if (lua_gettop (L) > 2) { // Add
+		child = lm_message_node_add_child (object->message_node, name,
+						   luaL_checkstring (L, 3));
+		lua_pop (L, 1);
+	} else { // Get
+		child = lm_message_node_get_child (object->message_node, name);
+		if (!child) {
+			lua_pushnil (L);
+			return 1;
+		}
+	}
+	llm_message_node_bless (L, child);
+	// XXX lm_message_node_unref (child);
+	lua_remove (L, -2);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:find_child
+/// Searches for node with a given name over all node subtree.
+/// A: string (name)
+/// R: lm message node object or nil
+static int llm_message_node_find_child (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	const char *name = luaL_checkstring (L, 2);
+	LmMessageNode *child;
+
+	child = lm_message_node_get_child (object->message_node, name);
+	if (!child)
+		lua_pushnil (L);
+	else {
+		llm_message_node_bless (L, child);
+		// XXX lm_message_node_unref (child);
+	}
+	lua_remove (L, -2);
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:raw
+/// Gets or sets raw mode flag for node.
+/// When set, value of node will not be escaped.
+/// A: boolean (optional)
+/// V: boolean (if called with no apguments)
+static int llm_message_node_raw (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		luaL_checktype (L, 2, LUA_TBOOLEAN);
+		lm_message_node_set_raw_mode (object->message_node, lua_toboolean (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushboolean (L, lm_message_node_get_raw_mode (object->message_node));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// message_node:attribute
+/// Gets or sets value of node attribute with a given name.
+/// A: string (name), string (optional value)
+/// R: string (when called with one aprgument)
+static int llm_message_node_attribute (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	const char *name = luaL_checkstring (L, 2);
+	if (lua_gettop (L) > 2) { // Set
+		lm_message_node_set_attribute (object->message_node, name, luaL_checkstring (L, 3));
+		lua_pop (L, 3);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_message_node_get_attribute (object->message_node, name));
+		lua_remove (L, -2);
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// message_node:xml
+/// Returns node representation in xml.
+/// R: string
+static int llm_message_node_xml (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	lua_pushstring (L, lm_message_node_to_string (object->message_node));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// message_node:pointer
+/// Returns pointer to underlying C structure.
+/// R: lightuserdata
+static int llm_message_node_pointer (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	lua_pushlightuserdata (L, object->message_node);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_message_node_gc (lua_State *L)
+{
+	llm_message_node_t *object = luaL_checklm_message_node (L, 1);
+	lm_message_node_unref (object->message_node);
+	lua_pop (L, 1);
+	return 0;
+}
+
+static const luaL_Reg llm_message_node_reg_f[] = {
+	{ "bless", llm_message_node_bless_lua },
+	{ NULL,    NULL                       },
+};
+
+static const luaL_Reg llm_message_node_reg_m[] = {
+	{ "name",       llm_message_node_name       },
+	{ "next",       llm_message_node_next       },
+	{ "prev",       llm_message_node_prev       },
+	{ "children",   llm_message_node_children   },
+	{ "parent",     llm_message_node_parent     },
+	{ "value",      llm_message_node_value      },
+	{ "child",      llm_message_node_child      },
+	{ "find_child", llm_message_node_find_child },
+	{ "attribute",  llm_message_node_attribute  },
+	{ "raw",        llm_message_node_raw        },
+	{ "xml",        llm_message_node_xml        },
+	{ "pointer",    llm_message_node_pointer    },
+	{ "__gc",       llm_message_node_gc         },
+	{ NULL,         NULL                        },
+};
+
+int luaopen_lm_message_node (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.message_node");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_message_node_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.message_node", llm_message_node_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_message_node.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,10 @@
+
+#ifndef LLM_MESSAGE_NODE_H
+#define LLM_MESSAGE_NODE_H
+
+#include <lua.h>
+
+int luaopen_lm_message_node (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_proxy.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,194 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+#include <loudmouth/loudmouth.h>
+
+#include "util.h"
+#include "lm_types.h"
+
+/// lm.proxy
+/// Object, containing information about proxy-server for connection.
+/// Create object, set it's parameters, and then attach to connection
+/// with 'proxy' method.
+
+/// proxy type
+/// Stirng, specifying proxy-server type. The only type supported for now is http.
+/// V: http, none
+const string2enum_t llm_proxy_type[] = {
+	{ "http", LM_PROXY_TYPE_HTTP },
+	{ "none", LM_PROXY_TYPE_NONE },
+	{ NULL,   LM_PROXY_TYPE_HTTP },
+};
+
+/// lm.proxy.new
+/// Creates new proxy object.
+/// Note, you should specify either none of args or both.
+/// A: proxy type, string (optional proxy server name), integer (optional server port)
+/// R: lm proxy object
+static int llm_proxy_new (lua_State *L)
+{
+	int type = luaL_checkenum (L, 1, llm_proxy_type);
+	LmProxy *proxy;
+	if (lua_gettop (L) > 0) {
+		proxy = lm_proxy_new_with_server (type, luaL_checkstring (L, 2), luaL_checkint (L, 3));
+		lua_pop (L, 3);
+	} else {
+		proxy = lm_proxy_new (type);
+		lua_pop (L, 1);
+	}
+	llm_proxy_bless (L, proxy);
+	lm_proxy_unref (proxy); // XXX
+	return 1;
+}
+
+/// lm.proxy.bless
+/// Blesses given pointer to lm proxy object.
+/// A: lightuserdata (C lm proxy object)
+/// R: lm proxy object
+static int llm_proxy_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm proxy lightuserdata expected");
+	llm_proxy_bless (L, lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// proxy:type
+/// Gets or sets proxy server type.
+/// A: proxy type (optional)
+/// R: proxy type (when called with no args)
+static int llm_proxy_kind (lua_State *L)
+{
+	llm_proxy_t *proxy = luaL_checklm_proxy (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_proxy_set_type (proxy->proxy, luaL_checkenum (L, 2, llm_proxy_type));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		luaL_pushenum (L, lm_proxy_get_type (proxy->proxy), llm_proxy_type);
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// proxy:server
+/// Gets or sets proxy server name.
+/// A: string (optional)
+/// R: string (when called with no args)
+static int llm_proxy_server (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_proxy_set_server (object->proxy, luaL_checkstring (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_proxy_get_server (object->proxy));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// proxy:port
+/// Gets or sets proxy server port.
+/// A: integer (optional)
+/// R: integer (when called with no args)
+static int llm_proxy_port (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_proxy_set_port (object->proxy, luaL_checkint (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushnumber (L, lm_proxy_get_port (object->proxy));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// proxy:username
+/// Gets or sets username to authenticate to proxy server with.
+/// A: string (optional)
+/// R: string (when called with no args)
+static int llm_proxy_username (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_proxy_set_username (object->proxy, luaL_checkstring (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_proxy_get_username (object->proxy));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// proxy:password
+/// Gets or sets password to authenticate to proxy server with.
+/// A: string (optional)
+/// R: string (when called with no args)
+static int llm_proxy_password (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	if (lua_gettop (L) > 1) { // Set
+		lm_proxy_set_password (object->proxy, luaL_checkstring (L, 2));
+		lua_pop (L, 2);
+		return 0;
+	} else { // Get
+		lua_pushstring (L, lm_proxy_get_password (object->proxy));
+		lua_remove (L, -2);
+		return 1;
+	}
+}
+
+/// proxy:pointer
+/// Returns pointer to underlying C structure.
+/// R: lightuserdata
+static int llm_proxy_pointer (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	lua_pushlightuserdata (L, object->proxy);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_proxy_gc (lua_State *L)
+{
+	llm_proxy_t *object = luaL_checklm_proxy (L, 1);
+	lm_proxy_unref (object->proxy);
+	lua_pop (L, 1);
+	return 0;
+}
+
+static const luaL_Reg llm_proxy_reg_f[] = {
+	{ "new",   llm_proxy_new       },
+	{ "bless", llm_proxy_bless_lua },
+	{ NULL,    NULL                },
+};
+
+static const luaL_Reg llm_proxy_reg_m[] = {
+	{ "port",     llm_proxy_port     },
+	{ "server",   llm_proxy_server   },
+	{ "type",     llm_proxy_kind     },
+	{ "username", llm_proxy_username },
+	{ "password", llm_proxy_password },
+	{ "pointer",  llm_proxy_pointer  },
+	{ "__gc",     llm_proxy_gc       },
+	{ NULL,       NULL               },
+};
+
+int luaopen_lm_proxy (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.proxy");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_proxy_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.proxy", llm_proxy_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_proxy.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,14 @@
+
+#ifndef LLM_PROXY_H
+#define LLM_PROXY_H
+
+#include <lua.h>
+
+#include "util.h"
+
+string2enum_t *llm_proxy_type;
+
+int luaopen_lm_proxy (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_ssl.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,200 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <glib.h>
+#include <loudmouth/loudmouth.h>
+#include <stdio.h>
+
+#include "util.h"
+#include "lm_types.h"
+
+/// lm.ssl
+/// Object, containing information about ssl abilities for connection.
+/// Create, set parameters, and attach to connection with 'ssl' method.
+
+/// ssl status
+/// String, representing what problem have current ssl session.
+/// V: no cert found, untrusted cert, cert expired, cert not activated, cert hostname mismatch, cert fingerprint mismatch, generic error
+const string2enum_t llm_ssl_status[] = {
+	{ "no cert found",             LM_SSL_STATUS_NO_CERT_FOUND             },
+	{ "untrusted cert",            LM_SSL_STATUS_UNTRUSTED_CERT            },
+	{ "cert expired",              LM_SSL_STATUS_CERT_EXPIRED              },
+	{ "cert not activated",        LM_SSL_STATUS_CERT_NOT_ACTIVATED        },
+	{ "cert hostname mismatch",    LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH    },
+	{ "cert fingerprint mismatch", LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH },
+	{ "generic error",             LM_SSL_STATUS_GENERIC_ERROR             },
+	{ NULL,                        0                                       }, // XXX
+};
+
+/// ssl callback function
+/// User function, called when ssl error happens.
+/// XXX: add lm connection object to args? it is not in API, but can be useful,
+/// though, with upvalues it is not required.
+/// A: lm ssl object, ssl status
+/// R: boolean (false if connection process should be terminated)
+LmSSLResponse llm_ssl_callback (LmSSL *ssl, LmSSLStatus status, llm_callback_t *cb)
+{
+	int ret;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	llm_ssl_bless (cb->L, ssl);
+	// XXX lm_ssl_unref (ssl);
+	luaL_pushenum (cb->L, status, llm_ssl_status);
+	if (lua_pcall (cb->L, 2, 0, 0)) {
+		// XXX lua_error (cb->L);
+		lua_pop (cb->L, 1);
+		return LM_SSL_RESPONSE_CONTINUE;
+	}
+	ret = lua_toboolean (cb->L, -1);
+	lua_pop (cb->L, 1);
+	if (ret)
+		return LM_SSL_RESPONSE_CONTINUE;
+	else
+		return LM_SSL_RESPONSE_STOP;
+}
+
+static void string2fingerprint (const char *string, char *buffer)
+{
+	int i;
+	for (i = 0; i < 16; i++) {
+		int h = g_ascii_xdigit_value ((char)string[i*3]);
+		int l = g_ascii_xdigit_value ((char)string[i*3+1]);
+		buffer[i] = (char) ((h >= 0 && l >= 0) ? h*16 + l : 0);
+	}
+}
+
+/// lm.ssl.new
+/// Creates new ssl object for use with connection.
+/// You can specify server key fingerprint, callback function for error handling,
+/// both, or neither. Though, fingerprint should go before callback function.
+/// SSL fingerprint is a string like '01:23:45:67:89:AB:CD:EF:FE:DC:BA:98:76:54:32:10'.
+/// A: string (optional ssl fingerprint), ssl callback function (optional)
+/// R: lm ssl object
+static int llm_ssl_new (lua_State *L)
+{
+	int args = lua_gettop (L);
+	LmSSL *ssl;
+	if (args == 0)
+		ssl = lm_ssl_new (NULL, NULL, NULL, NULL);
+	else if (args == 1 && !lua_isfunction (L, 1)) {
+		gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+		const char *fingerprint = luaL_checkstring (L, 1);
+
+		if (lua_objlen (L, 1) > 46)
+			string2fingerprint (fingerprint, buffer);
+		ssl = lm_ssl_new (buffer, NULL, NULL, NULL);
+		lua_pop (L, 1);
+	} else {
+		llm_callback_t *cb;
+		gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+		if (args > 1) {
+			const char *fingerprint = luaL_checkstring (L, 1);
+			if (lua_objlen (L, 1) > 46)
+				string2fingerprint (fingerprint, buffer);
+			luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+		} else
+			luaL_argcheck (L, lua_isfunction (L, 1), 1, "function expected");
+		
+		cb = luaL_malloc (L, sizeof (llm_callback_t));
+		cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+		cb->L = L;
+
+		ssl = lm_ssl_new ((args > 1) ? buffer : NULL, (LmSSLFunction)llm_ssl_callback,
+							cb, (GDestroyNotify)llm_callback_destroy);
+		lua_pop (L, 1);
+	}
+	llm_ssl_bless (L, ssl);
+	lm_ssl_unref (ssl); // XXX
+	return 1;
+}
+
+/// lm.ssl.bless
+/// Blesses given pointer to lm ssl object.
+/// A: lightuserdata (C lm ssl object)
+/// R: lm ssl object
+static int llm_ssl_bless_lua (lua_State *L)
+{
+	luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm ssl lightuserdata expected");
+	llm_ssl_bless (L, lua_touserdata (L, 1));
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// lm.ssl.supported
+/// Indicates if SSL is supported by loudmouth library.
+/// R: boolean
+static int llm_ssl_supported (lua_State *L)
+{
+	lua_pushboolean (L, lm_ssl_is_supported ());
+	return 1;
+}
+
+/// ssl:fingerprint
+/// Returns fingerprint of remote server.
+/// R: string or nil
+static int llm_ssl_fingerprint (lua_State *L)
+{
+	char buffer[48];
+	llm_ssl_t *object = luaL_checklm_ssl (L, 1);
+	const gchar *fingerprint = lm_ssl_get_fingerprint (object->ssl);
+	if (fingerprint == NULL)
+		lua_pushnil (L);
+	else {
+		snprintf (buffer, 48,
+			  "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:"
+			  "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX",
+			  fingerprint[0], fingerprint[1], fingerprint[2], fingerprint[3],
+			  fingerprint[4], fingerprint[5], fingerprint[6], fingerprint[7],
+			  fingerprint[8], fingerprint[9], fingerprint[10], fingerprint[11],
+			  fingerprint[12], fingerprint[13], fingerprint[14], fingerprint[15]);
+		lua_pushlstring (L, buffer, 47);
+	}
+	lua_remove (L, -2);
+	return 1;
+}
+
+/// ssl:pointer
+/// Returns pointer to underlying C structure.
+/// R: lightuserdata
+static int llm_ssl_pointer (lua_State *L)
+{
+	llm_ssl_t *object = luaL_checklm_ssl (L, 1);
+	lua_pushlightuserdata (L, object->ssl);
+	lua_remove (L, -2);
+	return 1;
+}
+
+static int llm_ssl_gc (lua_State *L)
+{
+	llm_ssl_t *object = luaL_checklm_ssl (L, 1);
+	lm_ssl_unref (object->ssl);
+	lua_pop (L, 1);
+	return 0;
+}
+
+const static luaL_Reg llm_ssl_reg_f[] = {
+	{ "new",       llm_ssl_new       },
+	{ "bless",     llm_ssl_bless_lua },
+	{ "supported", llm_ssl_supported },
+	{ NULL,        NULL              },
+};
+
+const static luaL_Reg llm_ssl_reg_m[] = {
+	{ "fingerprint", llm_ssl_fingerprint },
+	{ "pointer",     llm_ssl_pointer     },
+	{ "__gc",        llm_ssl_gc          },
+	{ NULL,          NULL                },
+};
+
+int luaopen_lm_ssl (lua_State *L)
+{
+	luaL_newmetatable (L, "loudmouth.ssl");
+	lua_pushstring (L, "__index");
+	lua_pushvalue (L, -2);
+	lua_settable (L, -3);
+	luaL_register (L, NULL, llm_ssl_reg_m);
+	lua_pop (L, 1);
+	luaL_register (L, "lm.ssl", llm_ssl_reg_f);
+	return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_ssl.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,14 @@
+
+#ifndef LLM_SSL_H
+#define LLM_SSL_H
+
+#include <lua.h>
+
+#include "util.h"
+
+string2enum_t *llm_ssl_status;
+
+int luaopen_lm_ssl (lua_State *L);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_types.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,62 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <loudmouth/loudmouth.h>  // lm_*ref
+
+#include "lm_types.h"
+#include "util.h"
+
+void llm_callback_destroy (llm_callback_t *cb)
+{
+	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	luaL_free (cb->L, cb);
+}
+
+#define LLM_CHECK(WHAT, TYPE)                                                     \
+llm_##WHAT##_t *luaL_checklm_##WHAT (lua_State *L, int index)                     \
+{                                                                                 \
+	llm_##WHAT##_t *object = luaL_checkudata (L, index, "loudmouth." #WHAT);  \
+	luaL_argcheck (L, object != NULL, index, "loudmouth " #WHAT " expected"); \
+	return object;                                                            \
+}
+
+#define LLM_BLESS(WHAT, TYPE)                                                   \
+llm_##WHAT##_t *llm_##WHAT##_bless (lua_State *L, TYPE *WHAT)                   \
+{                                                                               \
+	llm_##WHAT##_t *object;                              /* top of stack */ \
+	lua_pushstring (L, LLM_OBJREGISTRY);       /* 1 registry table name  */ \
+	lua_rawget (L, LUA_REGISTRYINDEX);               /* 1 registry table */ \
+	lua_pushlightuserdata (L, WHAT);                 /* 2 light userdata */ \
+	lua_rawget (L, -2);                                  /* 2 object/nil */ \
+	if (!lua_isnil (L, -1)) {                                /* 2 object */ \
+		lua_remove (L, -2);                              /* 1 object */ \
+		object = lua_touserdata (L, -1);                                \
+		return object;                                                  \
+	}                                                                       \
+	                                                            /* 2 nil */ \
+	lua_remove (L, -1);                              /* 1 registry table */ \
+	object = lua_newuserdata (L, sizeof (llm_##WHAT##_t)); /* 2 userdata */ \
+	luaL_getmetatable (L, "loudmouth." #WHAT);            /* 3 metatable */ \
+	lua_setmetatable (L, -2);                                /* 2 object */ \
+	lua_pushlightuserdata (L, WHAT);                 /* 3 light userdata */ \
+	lua_pushvalue (L, -2);                                   /* 4 object */ \
+	lua_rawset (L, -4);                                      /* 2 object */ \
+	lua_remove (L, -2);                                      /* 1 object */ \
+	object->WHAT = WHAT;                                                    \
+	lm_##WHAT##_ref (WHAT);                                                 \
+	return object;                                                          \
+}
+
+#define LLM_DEFINE(WHAT, TYPE) \
+LLM_CHECK (WHAT, TYPE)         \
+LLM_BLESS (WHAT, TYPE)
+
+LLM_DEFINE (connection, LmConnection)
+LLM_DEFINE (message, LmMessage)
+LLM_DEFINE (message_handler, LmMessageHandler)
+LLM_DEFINE (message_node, LmMessageNode)
+LLM_DEFINE (proxy, LmProxy)
+LLM_DEFINE (ssl, LmSSL)
+
+#undef LLM_DEFINE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lm_types.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,37 @@
+
+#ifndef LLM_TYPES_H
+#define LLM_TYPES_H
+
+#include <lua.h>
+#include <loudmouth/loudmouth.h>
+
+#ifndef LLM_OBJREGISTRY
+#define LLM_OBJREGISTRY ( "llm.obj_registry" )
+#endif
+
+typedef struct {
+	int        reference;
+	lua_State *L;
+} llm_callback_t;
+
+void llm_callback_destroy (llm_callback_t *cb);
+
+#define LLM_DECLARE(WHAT, TYPE)                                               \
+typedef struct {                                                              \
+	TYPE *WHAT;                                                           \
+} llm_##WHAT##_t;                                                             \
+                                                                              \
+llm_##WHAT##_t *luaL_checklm_##WHAT (lua_State *L, int index);                \
+llm_##WHAT##_t *llm_##WHAT##_bless (lua_State *L, TYPE *WHAT);
+
+LLM_DECLARE (connection, LmConnection)
+LLM_DECLARE (message, LmMessage)
+LLM_DECLARE (message_handler, LmMessageHandler)
+LLM_DECLARE (message_node, LmMessageNode)
+LLM_DECLARE (proxy, LmProxy)
+LLM_DECLARE (ssl, LmSSL)
+
+#undef LLM_DECLARE
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util.c	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,117 @@
+
+#include <glib.h>	// g_ascii_strcasecmp
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "util.h"
+
+enum_value_t string2enum (const char *string, const string2enum_t *set)
+{
+	while (set->string) {
+		if (!g_ascii_strcasecmp (string, set->string))
+			return set->value;
+		++set;
+	}
+	return set->value;
+}
+
+const char *enum2string (enum_value_t value, const string2enum_t *set)
+{
+	while (set->string) {
+		if (value == set->value)
+			return set->string;
+		++set;
+	}
+	return NULL;
+}
+
+enum_value_t luaL_checkenum (lua_State *L, int index, const string2enum_t *set)
+{
+	if (lua_type (L, index) == LUA_TNUMBER)
+		return lua_tointeger (L, index);
+	else
+		return string2enum (luaL_checkstring (L, index), set);
+}
+
+void luaL_pushenum (lua_State *L, enum_value_t value, const string2enum_t *set)
+{
+	const char *string = enum2string (value, set);
+	if (string != NULL)
+		lua_pushstring (L, string);
+	else
+		lua_pushinteger (L, value);
+}
+
+enum_value_t luaL_checkenum_multi (lua_State *L, int index, const string2enum_t *set)
+{
+	int type = lua_type (L, index);
+	if (type == LUA_TNUMBER)
+		return lua_tointeger (L, index);
+	else if (type == LUA_TSTRING)
+		return string2enum (lua_tostring (L, index), set);
+	else if (type == LUA_TTABLE) {
+		enum_value_t retval = 0;
+		lua_pushnil (L);
+		while (lua_next (L, index) != 0) {
+			type = lua_type (L, -2);
+			if (type == LUA_TNUMBER) {
+				type = lua_type (L, -1);
+				if (type == LUA_TNUMBER)
+					retval |= lua_tointeger (L, -1);
+				else if (type == LUA_TSTRING)
+					retval |= string2enum (lua_tostring (L, -1), set);
+				else
+					luaL_argerror (L, index, "wrong value type of flag");
+			} else if (type == LUA_TSTRING) {
+				if (lua_toboolean (L, -1))
+					retval |= string2enum (lua_tostring (L, -2), set);
+			} else
+				luaL_argerror (L, index, "wrong key type of flags table");
+			lua_pop (L, 1);
+		}
+	} else
+		luaL_argerror (L, index, "integer, string, or table of ones expected");
+	return 0; // never happens
+}
+
+void luaL_pushenum_multi (lua_State *L, enum_value_t value, const string2enum_t *set)
+{
+	enum_value_t matched = 0;
+	lua_newtable (L);
+	while (set->string) {
+		if (value & set->value) {
+			matched |= set->value & value;
+			lua_pushstring (L, set->string);
+			lua_pushboolean (L, 1);
+			lua_settable (L, -3);
+		}
+		++set;
+	}
+	if (value ^ matched) {
+		lua_pushinteger (L, 1);
+		lua_pushinteger (L, value ^ matched);
+		lua_settable (L, -3);
+	}
+}
+
+void *luaL_malloc (lua_State *L, size_t size)
+{
+	void      *ud;
+	lua_Alloc  allocf = lua_getallocf (L, &ud);
+	return (*allocf) (ud, NULL, 0, size);
+}
+
+void *luaL_realloc (lua_State *L, void *ptr, size_t osize, size_t nsize)
+{
+	void      *ud;
+	lua_Alloc  allocf = lua_getallocf (L, &ud);
+	return (*allocf) (ud, ptr, osize, nsize);
+}
+
+void  luaL_free (lua_State *L, void *ptr)
+{
+	void      *ud;
+	lua_Alloc  allocf = lua_getallocf (L, &ud);
+	(*allocf) (ud, ptr, 1, 0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util.h	Sun Feb 01 21:28:57 2009 +0200
@@ -0,0 +1,42 @@
+
+#ifndef MISC_LUA_UTIL_H
+#define MISC_LUA_UTIL_H
+
+#include <lua.h>
+
+#ifndef enum_value_t
+#define enum_value_t int
+#endif
+
+// Array of string2eunm_t's must be ended with pair { NULL, fallback_value }
+typedef struct {
+	const char   *string;
+	enum_value_t  value;
+} string2enum_t;
+
+// If not found, fallback_value is returned
+enum_value_t  string2enum (const char *string, const string2enum_t *set);
+// If not found, NULL is returned
+const char   *enum2string (enum_value_t value, const string2enum_t *set);
+
+// returns result of string2enum on specified stack index, can handle plain numbers (no s2e called in this case)
+enum_value_t luaL_checkenum (lua_State *L, int index, const string2enum_t *set);
+// pushes onto a stack result of enum2string on a given value, if not recognized, pushes value as number
+void         luaL_pushenum (lua_State *L, enum_value_t value, const string2enum_t *set);
+
+// as luaL_checknum, but additionally handles tables as a multiple flags set.
+// table can contain both array and hash key-value pairs
+// hash entries are ORed with string2enum(key) if value resolves to lua true value
+// array entries are always ORed with string2enum(value)
+// eg { flag1, flag2, 16, flag3 = true, flag4 = { }, flag5 = nil } will set all flags except flag5 (and 16 too will be ORed)
+enum_value_t luaL_checkenum_multi (lua_State *L, int index, const string2enum_t *set);
+// pushes to stack table with all matched values from a set in a hash format
+// eg { flag1 = true, flag2 = true, flag6 = true, 16 } - that's it, if some bits will not match, they will be passed as a first array element.
+void         luaL_pushenum_multi (lua_State *L, enum_value_t value, const string2enum_t *set);
+
+void *luaL_malloc (lua_State *L, size_t size);
+void *luaL_realloc (lua_State *L, void *ptr, size_t osize, size_t nsize);
+void  luaL_free (lua_State *L, void *ptr);
+
+#endif
+