lm.lua
changeset 0 84fdfb0344c9
child 6 90073cbb535d
--- /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: --