--- /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
+