Lm separation, privacy
authorMyhailo Danylenko <isbear@ukrpost.net>
Tue, 31 Mar 2009 18:35:34 +0300
changeset 68 742878c74b8e
parent 67 d33ca5572e91
child 69 ab6d4ee8974c
Lm separation, privacy * Library parts moved to lm.* * mc_* renamed to plain names * Privacy lists suppart (not tested)
examples/activity.lua
examples/attention.lua
examples/avatar.lua
examples/disco.lua
examples/evil.lua
examples/form_field.lua
examples/forms.lua
examples/geoloc.lua
examples/ibb.lua
examples/iq.lua
examples/iq_register.lua
examples/lm/activity.lua
examples/lm/attention.lua
examples/lm/avatar.lua
examples/lm/disco.lua
examples/lm/evil.lua
examples/lm/form_field.lua
examples/lm/geoloc.lua
examples/lm/ibb.lua
examples/lm/iq.lua
examples/lm/iq_register.lua
examples/lm/mood.lua
examples/lm/oob.lua
examples/lm/pep.lua
examples/lm/ping.lua
examples/lm/privacy.lua
examples/lm/pubsub.lua
examples/lm/remote.lua
examples/lm/tune.lua
examples/lm/vcard.lua
examples/lm/x_data.lua
examples/mc_activity.lua
examples/mc_attention.lua
examples/mc_avatar.lua
examples/mc_disco.lua
examples/mc_evil.lua
examples/mc_forms.lua
examples/mc_geoloc.lua
examples/mc_ibb.lua
examples/mc_mood.lua
examples/mc_oob.lua
examples/mc_ping.lua
examples/mc_pubsub.lua
examples/mc_register.lua
examples/mc_remote.lua
examples/mc_tune.lua
examples/mc_vcard.lua
examples/mcabberrc.lua
examples/mood.lua
examples/oob.lua
examples/pep.lua
examples/ping.lua
examples/privacy.lua
examples/pubsub.lua
examples/register.lua
examples/remote.lua
examples/tune.lua
examples/vcard.lua
examples/x_data.lua
--- a/examples/activity.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/activity.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,28 +1,56 @@
 
--- USER ACTIVITY (XEP-0108)
-
--- library
-
-local pep    = require 'pep'
-
--- public
-
-local F = { }
+local lm       = require 'lm'
+local activity = require 'lm.activity'
+local pubsub   = require 'lm.pubsub'
 
-function F.publish ( conn, success, fail, general, specific, message )
-	local item = { xmlns = 'http://jabber.org/protocol/activity' }
-	if general then
-		item[general] = { }
-		if specific then
-			item[general][specific] = { }
+pubsub.handler ( 'http://jabber.org/protocol/activity',
+	function ( from, node, data )
+		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+			return true
+		end
+		local item = data:child ()
+		local activity, desc
+		while item do
+			if item:name () == 'text' then
+				desc = item:value ()
+			else
+				activity = item:name ()
+				local subitem = item:child ()
+				if subitem then
+					-- here we can check for non-standard subactivity elements,
+					-- add subactivity child elements handling
+					activity = ("%s: %s"):format ( activity, subitem:name () )
+				end
+			end
+			item = item:next ()
 		end
-	end
-	if message then
-		item.text = { message }
-	end
-	pep.publish ( conn, 'http://jabber.org/protocol/activity', { activity = item }, success, fail )
-end
+		if activity then
+			main.print_info ( from, ("Now %s %s"):format ( activity, desc or '' ) )
+		else
+			main.print_info ( from, "Buddy hides his activity" )
+		end
+		return true
+	end )
 
-return F
+main.command ( 'activity',
+	function ( args )
+		local a, text = args[1], args[2]
+		local act, subact = a:match ( "(.-)%-(.+)" )
+		if not act then
+			act = a
+		end
+		activity.publish ( lm.connection.bless ( main.connection () ),
+			function ()
+				print ( 'Activity published' )
+			end,
+			function ( mesg )
+				print ( 'Error publishing activity: ' .. mesg )
+			end, act, subact, text )
+	end, true )
+
+commands_help['activity'] = "[activity[-specific_activity] [text]]\n\nPublishes your activity.\nNote, that for now it does not checks for activity validity, so, see xep0108 for valid activity values."
+
+main.add_feature ( 'http://jabber.org/protocol/activity+notify' )
+main.add_feature ( 'http://jabber.org/protocol/activity' )
 
 -- vim: se ts=4: --
--- a/examples/attention.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/attention.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,48 +1,50 @@
 
--- ATTENTION (XEP-0224)
+local lm        = require 'lm'
+local attention = require 'lm.attention'
 
--- library
-
-local lm = require 'lm'
-
---
+attention.handler (
+	function ( mesg )
+		local times = 0
+		main.timer ( 1,
+			function ()
+				if times < 6 then
+					main.beep ()
+					times = times + 1
+					return true
+				end
+				return false
+			end )
+	end )
 
-local O = {
-	handler =
-		function ( mesg )
-		end,
-}
+main.command ( 'attention',
+	function ( args )
+		local who
+		if args.t then
+			who = args.t
+		else
+			who = main.full_jid ()
+		end
+		attention.send ( lm.connection.bless ( main.connection () ), who, args[1] )
+	end, true, 'jid' )
+
+commands_help['attention'] = "[-t to] [message]\n\nTries to get buddy's attention."
 
-local F = { }
+local attention_handler = lm.message_handler.new ( attention.message_handler )
+local attention_handler_registered = false
 
-function F.send ( conn, to, message )
-	local body = nil
-	if message then
-		body = { message }
+hooks_d['hook-post-connect'].attention =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( attention_handler, 'message', 'normal' )
+		attention_handler_registered = true
+		hooks_d['hook-post-connect'].attention = nil
+		hooks_d['hook-quit'].attention =
+			function ( args )
+				if attention_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( attention_handler, 'message' )
+				end
+			end
 	end
-	conn:send (
-		lm.message.create { mtype = 'message-headline', to = to,
-			attention = { xmlns = 'urn:xmpp:attention:0' },
-			body = body,
-		} )
-end
 
-function F.handler ( handler )
-	O.handler = handler
-end
-
-function F.message_handler ( conn, mess )
-	local a = mess:child ( 'attention' )
-	if a and a:attribute ( 'xmlns' ) == 'urn:xmpp:attention:0' then
-		local body = mess:child ( 'body' )
-		if body then
-			body = body:value ()
-		end
-		O.handler ( body )
-	end
-	return false
-end
-
-return F
+main.add_feature ( 'urn:xmpp:attention:0' )
 
 -- vim: se ts=4: --
--- a/examples/avatar.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/avatar.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,58 +1,74 @@
 
--- USER AVATAR (XEP-0084)
-
--- library
-
-local sha1   = require 'sha1'
-local base64 = require 'base64'
 local lm     = require 'lm'
-local pep    = require 'pep'
-local pubsub = require 'pubsub'
-
---
+local pubsub = require 'lm.pubsub'
+local avatar = require 'lm.avatar'
 
-local F = { }
-
--- TODO 'temporary disabling'
---          however I cannot see a method to enable it back without republishing avatar :(
---          this requires client to know, what is published on the server now
---          maybe we can do that by requesting item without payload from server
+pubsub.handler ( 'urn:xmpp:avatar:metadata',
+	function ( from, node, data, id )
+		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+			return true
+		end
+		local item = data:child ()
+		while item do
+			main.print_info ( from, ('Avatar: %s [%s] %s bytes, %sx%s %s'):format (
+					item:attribute ( 'id' ),
+					item:attribute ( 'type' ),
+					item:attribute ( 'bytes' ),
+					item:attribute ( 'width' ) or '?',
+					item:attribute ( 'height' ) or '?',
+					item:attribute ( 'url' ) or '' ) )
+			item = item:next ()
+		end
+	end )
 
-function F.publish ( conn, data, success, fail, height, width )
-	local id = sha1.digest ( data )
-	pep.publish ( conn, 'urn:xmpp:avatar:data',
-		{ id = id,
-			data = { xmlns = 'urn:xmpp:avatar:data',
-				base64.encode ( data ),
-			},
-		},
-		function ()
-			pep.publish ( conn, 'urn:xmpp:avatar:metadata',
-				{ id = id,
-					info = { bytes = data:len (), id = id, type = 'image/png', height = height, width = width },
-				}, success, fail )
-		end, fail )
-end
+main.command ( 'avatar',
+	function ( args )
+		local action = args[1]
+		if action == 'get' then
+			local who
+			if args.t then
+				who = args.t
+			else
+				who = main.current_buddy ()
+			end
+			avatar.get ( lm.connection.bless ( main.connection () ), who, args[2],
+				function ( data )
+					local h = io.open ( args[3], 'w' )
+					if h then
+						h:write ( data )
+						h:close ()
+						main.print_info ( who, 'Avatar saved to ' .. args[3] )
+					else
+						print ( 'Cannot open file for writing ' .. args[3] )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Error obtaining avatar: ' .. mesg )
+				end )
+		else
+			local file = action
+			if action == 'set' then
+				file = args[2]
+			end
+			local h = io.open ( file )
+			if h then
+				data = h:read ( '*a' )
+				h:close ()
+				avatar.publish ( lm.connection.bless ( main.connection () ), data,
+					function ()
+						print ( 'Avatar published' )
+					end,
+					function ( mesg )
+						print ( 'Avatar publishing error: ' .. mesg )
+					end )
+			else
+				print ( 'Cannot open file ' .. file )
+			end
+		end
+	end, true, 'file' )
 
-function F.publish_url ( conn, url, iid, size, id, mtype, success, fail, height, width )
-	pep.publish ( conn, 'urn:xmpp:avatar:metadata',
-		{ id = iid,
-			info = { bytes = size, id = id, type = mtype, url = url, height = height, width = width },
-		}, success, fail )
-end
+commands_help['avatar'] = '[-t jid] get id filename | [set] filename\n\nGet action tries to get from server avatar with specified id and save it to \'filename\'.\nSet action publishes avatar to server. File must be a PNG image.'
 
-function F.get ( conn, from, id, success, fail )
-	pubsub.retrieve ( conn, from, 'urn:xmpp:avatar:data',
-		function ( from, node, item )
-			local data = item:child ( 'data' )
-			if data then
-				success ( data:value () )
-			else
-				-- XXX
-			end
-		end, fail, nil, { id } )
-end
-
-return F
+main.add_feature ( 'urn:xmpp:avatar:metadata+notify' )
 
 -- vim: se ts=4: --
--- a/examples/disco.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/disco.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,58 +1,62 @@
 
--- SERVICE DISCOVERY (XEP-0030)
-
--- TODO add handler (unused by mcabber)
-
--- library
-
-local iq = require 'iq'
-
---
-
-local F = { }
+local lm    = require 'lm'
+local disco = require 'lm.disco'
 
-function F.items ( conn, to, success, fail, node )
-	iq.send ( conn, to, 'get',
-		{
-			query = { xmlns = 'http://jabber.org/protocol/disco#items', node = node }
-		},
-		function ( mess )
-			local item  = mess:child( 'query' ):child ()
-			local items = { }
-			while item do
-				if item:name () == 'item' then
-					table.insert ( items, { jid = item:attribute ( 'jid' ), node = item:attribute ( 'node' ), name = item:attribute ( 'name' ) } )
-				end
-				item = item:next ()
-			end
-			success ( items )
-		end,
-		fail )
-end
+main.command ( 'disco',
+	function ( args )
+		local who
+		local conn = lm.connection.bless ( main.connection () )
+		if args.t then
+			who = args.t
+		else
+			who = main.full_jid ()
+		end
+		if args[1] == 'items' then
+			local node = args[2]
+			disco.items ( conn, who,
+				function ( items )
+					local text = ''
+					for index, item in ipairs ( items ) do
+						text = text .. ("\n    [%s (%s)] %s"):format ( item.jid or '', item.node or '', item.name or '' )
+					end
+					if text ~= '' then
+						main.print_info ( who, ("Items service discovery result for %s (%s):%s"):format ( who, node or '', text ) )
+					else
+						main.print_info ( who, ("No items in discovery result for %s (%s)"):format ( who, node or '' ) )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, ("Items service discovery for %s (%s) failed: %s"):format ( who, node or '', mesg ) )
+				end, node )
+		else
+			disco.info ( conn, who,
+				function ( identities, features )
+					main.print_info ( who, ("Service info discovery result for %s:"):format ( who ) )
+					local text = ''
+					for index, identity in ipairs ( identities ) do
+						text = text .. ("\n    [%s (%s)] %s"):format ( identity.category or '', identity.type or '', identity.name or '' )
+					end
+					if text ~= '' then
+						main.print_info ( who, "  Identities:" .. text )
+					else
+						main.print_info ( who, "  No identities" )
+					end
+					text = ''
+					for index, feature in ipairs ( features ) do
+						text = text .. ("\n    [%s]"):format ( feature or '' )
+					end
+					if text ~= '' then
+						main.print_info ( who, "  Features:" .. text )
+					else
+						main.print_info ( who, "  No features" )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, ("Info service discovery for %s failed: %s"):format ( who, mesg ) )
+				end )
+		end
+	end, true, 'jid' )
 
-function F.info ( conn, to, success, fail )
-	iq.send ( conn, to, 'get',
-		{
-			query = { xmlns='http://jabber.org/protocol/disco#info' }
-		},
-		function ( mess )
-			local identities = { }
-			local features   = { }
-			local item       = mess:child( 'query' ):child ()
-			while item do
-				local name  = item:name ()
-				if name == 'identity' then
-					table.insert ( identities, { category = item:attribute ( 'category' ), type = item:attribute ( 'type' ), name = item:attribute ( 'name' ) } )
-				elseif name == 'feature' then
-					table.insert ( features, item:attribute ( 'var' ) )
-				end
-				item = item:next ()
-			end
-			success ( identities, features )
-		end,
-		fail )
-end
-
-return F
+commands_help['disco'] = "[-t target_jid] [info | items] [node]\n\nService discovery request.\nInfo is sent if omitted."
 
 -- vim: se ts=4: --
--- a/examples/evil.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/evil.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,58 +1,110 @@
 
--- MALICIOUS STANZAS (XEP-0076)
-
--- library
-
-local lm = require 'lm'
-local iq = require 'iq'
+local lm   = require 'lm'
+local evil = require 'lm.evil'
 
--- public
+evil.handler (
+	function ( mess )
+		local evillevel = tonumber(main.option ( 'lua_evil_sensibility' ))
+		local mtype, smtype = mess:type ()
+		if evillevel > 1 then
+			main.print_info ( mess:attribute ( 'from' ), 'Evil stanza of type ' .. mtype .. '.' .. smtype .. ' detected!' )
+		elseif evillevel > 0 then
+			print ( 'Tainted by evil stanza of type ' .. mtype .. '.' .. smtype .. ' from ' .. ( mess:attribute ( 'from' ) or '... unknown in black' ) )
+		end
+		return main.yesno ( main.option ( 'lua_filter_evil' ) )
+	end )
 
-local O = {
-	handler =
-		function ( mess )
-			return false
-		end,
+local stat2xmpp = {
+	free     = 'chat',
+	online   = '',
+	away     = 'away',
+	dnd      = 'dnd',
+	notavail = 'xa',
+	offline  = 'unavailable',
 }
 
-local F = { }
-
-function F.handler ( handler )
-	O.handler = handler
-end
+-- TODO improve interface, check if we sending right thing for offline
+main.command ( 'evil',
+	function ( args )
+		local conn = lm.connection.bless ( main.connection () )
+		if args[1] == 'status' then
+			local text = ''
+			for i, mesg in ipairs ( args ) do
+				if i > 2 then
+					text = text .. ' ' .. mesg
+				end
+			end
+			local st = stat2xmpp[args[2]]
+			if not st then
+				st = ''
+			end
+			evil.presence ( conn, args.t, st, text:sub ( 2 ) )
+		else
+			local text = ''
+			if args[1] == 'message' then
+				for i, mesg in ipairs ( args ) do
+					if i > 1 then
+						text = text .. ' ' .. mesg
+					end
+				end
+			else
+				for i, mesg in ipairs ( args ) do
+					text = text .. ' ' .. mesg
+				end
+			end
+			local mtype = 'chat'
+			if args.k then
+				mtype = args.k
+			end
+			local who
+			if args.t then
+				who = args.t
+			else
+				who = main.current_buddy ()
+			end
+			evil.message ( conn, who, mtype, text:sub ( 2 ) )
+		end
+	end, true )
 
-function F.message ( conn, to, mtype, message )
-	conn:send ( lm.message.create { mtype = 'message-' .. mtype, to = to,
-			body = { message },
-			evil = { xmlns = 'http://jabber.org/protocol/evil' },
-		} )
-end
+commands_help['evil'] = "[-t jid] [status stat [message] | [-k message_type] [message] message]\n\nSends evil message or presence.\nmessage_type may be chat, normal, headline.\nNote, that for now it will not change mcabber's status."
+
+local evil_handler = lm.message_handler.new ( evil.stanza_handler )
+local evil_handler_registered = false
 
-function F.presence ( conn, to, status, message )
-	local mtype = 'presence-available'
-	if status == 'unavailable' then
-		mtype = 'presence-unavailable'
-		status = ''
+hooks_d['hook-post-connect'].evil =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( evil_handler, 'iq',       'first' )
+		lm.connection.bless( main.connection () ):handler ( evil_handler, 'message',  'first' )
+		lm.connection.bless( main.connection () ):handler ( evil_handler, 'presence', 'first' )
+		evil_handler_registered = true
+		hooks_d['hook-post-connect'].evil = nil
+		hooks_d['hook-quit'].evil =
+			function ( args )
+				if evil_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( evil_handler, 'iq'       )
+					lm.connection.bless( main.connection () ):handler ( evil_handler, 'message'  )
+					lm.connection.bless( main.connection () ):handler ( evil_handler, 'presence' )
+				end
+			end
 	end
-	conn:send ( lm.message.create { mtype = mtype, from = conn:jid (), to = to,
-			show   = { status },
-			status = { message },
-			evil   = { xmlns = 'http://jabber.org/protocol/evil' },
-		} )
-end
+
+local char2xmpp = {
+	f = 'chat',
+	o = '',
+	a = 'away',
+	d = 'dnd',
+	n = 'xa',
+	_ = 'unavailable',
+}
 
-function F.iq ( conn, to, action, contents, success, fail )
-	contents.evil = { xmlns = 'http://jabber.org/protocol/evil' }
-	iq.send ( conn, to, action, contents, success, fail )
-end
+-- hack, but working ;)
+hooks_d['hook-my-status-change'].evil =
+	function ( args )
+		if main.yesno ( main.option ( 'lua_evil_mode' ) ) then
+			evil.presence ( lm.connection.bless ( main.connection () ), nil, char2xmpp[args.new_status], args.message )
+		end
+	end
 
-function F.stanza_handler ( conn, mess )
-	local e = mess:child ( 'evil' )
-	if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/evil' then
-		return O.handler ( mess )
-	end
-end
-
-return F
+main.add_feature ( 'http://jabber.org/protocol/evil' )
 
 -- vim: se ts=4: --
--- a/examples/form_field.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-
--- DATA FORM FIELD
-
-local F = { }
-local M = { }
-M.__index = M
-
--- field.new      -- create new field
--- field:index    -- get index
--- field:type     -- get type
--- field:name     -- get var
--- field:desc     -- get label, desc
--- field:value    -- get/set value
--- field:values   -- get values
--- field:clear    -- clear value
--- field:options  -- get options
--- field:required -- get required
-
-function F.new ( args )
-	local obj = {
-		t = args.type,
-		l = args.label,
-		d = args.desc,
-		o = args.options,  -- XXX
-		v = args.value,    -- XXX
-		r = args.required,
-		n = args.var,
-		i = args.index,    -- XXX
-	}
-	setmetatable ( obj, M )
-	return obj
-end
-
-function M.index ( obj )
-	return obj.i
-end
-
-function M.type ( obj )
-	return obj.t
-end
-
-function M.name ( obj )
-	return obj.n
-end
-
-function M.desc ( obj )
-	return obj.l, obj.d
-end
-
-function M.options ( obj )
-	return pairs ( obj.o )
-end
-
-function M.value ( obj, value )
-	local ftype = obj.t
-	if value == nil then
-		-- XXX
-		return obj.v
-	else
-		if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
-			table.insert ( obj.v, value )
-		else
-			obj.v = value
-		end
-	end
-end
-
-function M.values ( obj )
-	local ftype = obj.t
-	if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
-		return ipairs ( obj.v )
-	else
-		return
-			function ( arg )
-				if not arg then
-					return obj.v
-				else
-					return nil
-				end
-			end, nil
-	end
-end
-
-function M.clear ( obj )
-	local ftype = obj.t
-	if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
-		obj.v = { }
-	else
-		obj.v = ''
-	end
-end
-
-function M.required ( obj )
-	return obj.r
-end
-
-return F
-
--- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/forms.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,114 @@
+
+local form_cid = main.add_category { 'del', 'send' }
+local forms = { }
+
+-- public
+function insert_form ( form, submit, reject )
+	table.insert ( forms, {
+			form   = form,
+			submit = submit,
+			reject = reject,
+		} )
+	main.add_completion ( form_cid, tostring(#forms) )
+	print ( "You have new form. Use /form " .. #forms .. " to fill and submit or cancel it." )
+	return #forms
+end
+
+main.command ( 'form',
+	function ( args )
+		local id, action = tonumber (args[1]), args[2]
+		if forms[id] then
+			local form = forms[id].form
+			if action == 'send' then
+				forms[id].submit ( form )
+			elseif action == 'reject' then
+				forms[id].reject ( form )
+			elseif action == 'del' then
+				main.del_completion ( form_cid, id )
+				forms[id] = nil
+			elseif action then
+				local fname, value
+				if action == 'set' then
+					fname = args[3]
+					value = args[4]
+				else
+					fname = action
+					value = args[3]
+				end
+				local field = form:field ( fname )
+				if field then
+					if value then
+						field:value ( value )
+					else
+						field:clear ()
+					end
+				else
+					-- XXX create?
+					print ( 'Field not found: ' .. fname )
+				end
+			else
+				local desc = 'Form ' .. id .. '\n - Type: ' .. form:type ()
+				local title, instructions = form:desc ()
+				if title then
+					desc = desc .. '\n - Title: ' .. title
+				end
+				if instructions then
+					desc = desc .. '\n - Instructions: ' .. instructions
+				end
+
+				local fields = 'Fields:'
+				for index, field in form:fields () do
+					local ftype = field:type ()
+
+					fields = fields .. '\n - '
+					
+					if field:required () then
+						fields = fields .. '* '
+					end
+
+					local label, descr = field:desc ()
+					if label then
+						fields = fields .. label .. ' (' .. ( field:name () or '' ) .. ')'
+					else
+						fields = fields .. ( field:name () or '<no name>' )
+					end
+					fields = fields .. ' [' .. ftype .. ']'
+					if descr then
+						fields = fields .. ': ' .. descr
+					end
+
+					if ftype == 'list-single' or ftype == 'list-multi' then
+						fields = fields .. '\n   Options:'
+						for option, label in field:options () do
+							fields = fields .. '\n    * ' .. option .. ' - ' .. label
+						end
+					end
+					
+					if ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'jid-multi' then
+						fields = fields .. '\n   Values: '
+						for vin, value in field:values () do
+							fields = fields .. '\n    * ' .. value
+						end
+					else
+						fields = fields .. '\n   Value: ' .. ( field:value () or '' )
+					end
+				end
+				print ( desc .. '\n' .. fields )
+			end
+		else
+			local text = ''
+			for id, form in pairs ( forms ) do
+				local title = form.form:desc ()
+				text = text .. '\n - ' .. id .. ' ' .. ( title or 'Untitled' ) .. ' [' .. form.form:type () .. ']'
+			end
+			if text ~= '' then
+				print ( 'Forms list:' .. text )
+			else
+				print ( 'No forms' )
+			end
+		end
+	end, true, form_cid )
+
+commands_help['form'] = "[form_id [send | reject | [set] fieldname [value]]]\n\nWithout arguments prints form list.\nWith bare form id prints info on that form.\nWhen setting multivalue field, new values are added, not replacing previous.\nWithout value unsets field, multivalue fields lose all their values."
+
+-- vim: se ts=4: --
--- a/examples/geoloc.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/geoloc.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,24 +1,42 @@
 
--- USER LOCATION (XEP-0080)
-
--- library
-
-local pep = require 'pep'
-
--- public
-
-local F = { }
+local lm     = require 'lm'
+local geoloc = require 'lm.geoloc'
+local pubsub = require 'lm.pubsub'
 
-function F.publish ( conn, success, fail, data )
-	local sdata = { xmlns = 'http://jabber.org/protocol/geoloc' }
-	if data then
-		for key, value in pairs ( data ) do
-			sdata[key] = { value }
+pubsub.handler ( 'http://jabber.org/protocol/geoloc',
+	function ( from, node, data )
+		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+			return true
+		end
+		local item = data:child ()
+		local text = ''
+		while item do
+			text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
+			item = item:next ()
+		end
+		if text ~= '' then
+			text = 'Now at:' .. text
+		else
+			text = 'Now in unknown location'
 		end
-	end
-	pep.publish ( conn, 'http://jabber.org/protocol/geoloc', { geoloc = sdata }, success, fail )
-end
+		main.print_info ( from, text )
+		return true
+	end )
 
-return F
+main.command ( 'location',
+	function ( args )
+		geoloc.publish ( lm.connection.bless ( main.connection () ),
+			function ()
+				print ( 'Geolocation published' )
+			end,
+			function ( mesg )
+				print ( 'Error geolocation publishing: ' .. mesg )
+			end, args )
+	end, true )
+
+commands_help['location'] = "[-key value [-key value ...]]\n\nPublishes your current geolocation.\nValid keys are accuracy, alt, area, bearing, building, country, datum, description, error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp and uri, according to xep0080."
+
+main.add_feature ( 'http://jabber.org/protocol/geoloc+notify' )
+main.add_feature ( 'http://jabber.org/protocol/geoloc' )
 
 -- vim: se ts=4: --
--- a/examples/ibb.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/ibb.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,188 +1,138 @@
-
--- IN-BAND BYTESTREAMS (XEP-0047)
 
--- TODO bidirectionality
---        thus, on stream accept we can add our sid to incoming files structure,
---        as if we received and accepted incoming request.
---      message stanzas
+local lm  = require 'lm'
+local ibb = require 'lm.ibb'
 
--- library
+local mc_incoming_files = { }
 
-local lm     = require 'lm'
-local iq     = require 'iq'
-local base64 = require 'base64'
-
---
+ibb.handler (
+	function ( from, accept, reject )
+		local fid = #mc_incoming_files + 1
+		mc_incoming_files[fid] = {
+				from   = from,
+				accept =
+					function ( name )
+						mc_incoming_files[fid].name = name
+						accept (
+							function ( data )
+								local h = io.open ( mc_incoming_files[fid].name, 'w' )
+								if not h then
+									print ( 'Cannot open output file: ' .. mc_incoming_files[fid].name )
+									return
+								end
+								h:write ( data )
+								h:close ()
+								print ( 'Stream ' .. fid .. ' successfully saved to ' .. mc_incoming_files[fid].name )
+								mc_incoming_files[fid] = nil
+							end,
+							function ( mesg )
+								main.print_info ( from, 'Stream error: ' .. mesg )
+								mc_incoming_files[fid] = nil -- XXX
+							end )
+					end,
+				reject =
+					function ()
+						reject ()
+						print ( 'Stream ' .. fid .. ' rejected' )
+						mc_incoming_files[fid] = nil
+					end,
+		}
+		main.print_info ( from, from .. ' wants you to receive stream. Use /ibb [accept|reject] ' .. fid .. ' to process his request.' )
+	end )
 
-local F = { }
-local M = { }
-M.__index = M
-local O = {
-	handler =
-		function ( accept, reject )
-			reject ()
-		end,
-}
+local ibb_sid = 0
 
-function F.new ( conn, to, bs, sid )
-	local obj = {
-		conn = conn,
-		to   = to,
-		sbs  = bs,
-		bs   = math.floor ( bs * 3 / 4 ),
-		sid  = sid,
-		seq  = 0,
-	}
-	setmetatable ( obj, M )
-	return obj
-end
-
-function M.open ( obj, success, fail )
-	iq.send ( obj.conn, obj.to, 'set',
-		{
-			open = { sid = obj.sid, ['block-size'] = obj.sbs, xmlns = 'http://jabber.org/protocol/ibb' }
-		}, success, fail )
-end
-
-function M.send ( obj, data, success, fail )
-	if data and data ~= '' then
-		local start = 0
-		while start < data:len () do
-			local chunk = base64.encode ( data:sub ( start, obj.bs ) )
-			local cseq  = obj.seq -- local instance
-			iq.send ( obj.conn, obj.to, 'set',
-				{
-					data = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb', seq = cseq,
-						chunk,
-					},
-				},
+main.command ( 'ibb',
+	function ( args )
+		local action = args[1]
+		if action == 'send' then
+			local who
+			if args.t then
+				who = args.t
+			else
+				who = main.full_jid ()
+			end
+			local fname = args[2]
+			local conn  = lm.connection.bless ( main.connection () )
+			local sid   = ibb_sid
+			ibb_sid     = ibb_sid + 1
+			local stream = ibb.new ( conn, who, 4096, sid )
+			stream:open (
 				function ()
-					success ( cseq )
+					main.print_info ( who, 'Stream accepted' )
+					local noerr = true
+					local h     = io.open ( fname, 'r' )
+					if not h then
+						print ( 'Cannot open file ' .. fname )
+						return
+					end
+					local data = h:read ( '*a' ) -- In fact, it is better to read it in chunks :/
+					h:close ()
+					local fail =
+						function ( mesg )
+							noerr = false
+							main.print_info ( who, 'Stream error: ' .. mesg )
+						end
+					stream:send ( data,
+						function ( seq )
+							main.print_info ( who, 'Delivery notification of chunk #' .. seq )
+						end, fail )
+					if noerr then
+						stream:close (
+							function ()
+								main.print_info ( who, 'Stream finalizing notification' )
+							end, fail )
+					end
+					if noerr then
+						main.print_info ( who, 'Stream sent' )
+					else
+						main.print_info ( who, 'Stream error occured' )
+					end
 				end,
 				function ( mesg )
-					noerr = false
-					fail ( mesg )
+					main.print_info ( who, 'Stream initiation error: ' .. mesg )
 				end )
-			start   = start + obj.bs
-			obj.seq = obj.seq + 1
+		elseif action == 'accept' then
+			local id = tonumber(args[2])
+			if mc_incoming_files[id] then
+				mc_incoming_files[id].accept ( args[3] )
+			end
+		elseif action == 'reject' then
+			local id = tonumber(args[2])
+			if mc_incoming_files[id] then
+				mc_incoming_files[id].reject ()
+			end
+		else
+			local text = ''
+			for sid, data in pairs ( mc_incoming_files ) do
+				text = text .. '\n' ..  sid .. ': ' .. data.from .. ' --> ' .. ( data.name or '?' )
+			end
+			if text ~= '' then
+				print ( 'List of incoming streams:' .. text )
+			else
+				print ( 'No streams' )
+			end
 		end
-	end
-end
+	end, true, { "send", "accept", "reject" } )
 
-function M.close ( obj, success, fail )
-	iq.send ( obj.conn, obj.to, 'set',
-		{
-			close = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb' },
-		}, success, fail )
-end
+
+commands_help['ibb'] = "[[-t target_jid] send filename | accept sid filename | reject sid]\n\nRequests, accepts or rejects sending file via in-band bytestream."
+
+local ibb_handler            = lm.message_handler.new ( ibb.iq_handler )
+local ibb_handler_registered = false
 
-function F.handler ( handler )
-	O.handler = handler
-end
-
-local ibb_files = {}
-
-function F.iq_handler ( conn, mess )
-	local mtype, smtype = mess:type ()
-	if smtype ~= 'set' then
-		return false
+hooks_d['hook-post-connect'].ibb =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq', 'normal' )
+		ibb_handler_registered = true
+		hooks_d['hook-post-connect'].ibb = nil
+		hooks_d['hook-quit'].ibb =
+			function ( args )
+				if ibb_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq' )
+				end
+			end
 	end
 
-	local child = mess:child ()
-	if not child or child:attribute ( 'xmlns' ) ~= 'http://jabber.org/protocol/ibb' then
-		return false
-	end
-
-	local id     = mess:attribute ( 'id' )
-	local from   = mess:attribute ( 'from' )
-	local action = child:name ()
-	local sid    = child:attribute ( 'sid' )
-
-	if action == 'open' then
-		if not ibb_files[sid] then
-			O.handler ( from,
-				function ( success, fail )
-					ibb_files[sid] = { from = from, success = success, fail = fail }
-					conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
-				end,
-				function ()
-					conn:send (
-						lm.message.create { to = from, mtype = 'iq-error', id = id,
-							error = { code = '405', type = 'cancel',
-								['not-allowed'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-							},
-						} )
-				end )
-		else
-			conn:send (
-				lm.message.create { to = from, mtype = 'iq-error', id = id,
-					error = { code = '409', type = 'cancel',
-						conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-					},
-				} )
-		end
-	elseif action == 'data' then
-		local seq = child:attribute ( 'seq' )
-		if ibb_files[sid] and from == ibb_files[sid].from and not ibb_files[sid][tonumber(seq)+1] then
-			local data = child:value ()
-			conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
-			ibb_files[sid][tonumber(seq)+1] = data
--- XXX		ibb_files[sid].success ( seq )
-		else
-			if ibb_files[sid] then
-				ibb_files[sid].fail ( 'conflict' )
-				ibb_files[sid] = nil -- invalidate session
-				conn:send (
-					lm.message.create { to = from, mtype = 'iq-error', id = id,
-						error = { code = '409', type = 'cancel',
-							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-						},
-					} )
-			else
-				conn:send (
-					lm.message.create { to = from, mtype = 'iq-error', id = id,
-						error = { code = '404', type = 'cancel', -- XXX: check
-							['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-						},
-					} )
-			end
-		end
-	elseif action == 'close' then
-		if ibb_files[sid] and from == ibb_files[sid].from then
-			local data = ''
-			for seq, chunk in ipairs ( ibb_files[sid] ) do
-				data = data .. chunk
-			end
-			local decoded = base64.decode ( data )
-			ibb_files[sid].success ( decoded )
-			conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
-			ibb_files[sid] = nil
-		else
-			if ibb_files[sid] then
-				ibb_files[sid].fail ( 'conflict' )
-				ibb_files[sid] = nil -- invalidate session
-				conn:send (
-					lm.message.create { to = from, mtype = 'iq-error', id = id,
-						error = { code = '409', type = 'cancel',
-							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-						},
-					} )
-			else
-				conn:send (
-					lm.message.create { to = from, mtype = 'iq-error', id = id,
-						error = { code = '404', type = 'cancel', -- XXX: check
-							['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-						},
-					} )
-			end
-		end
-	else
-		return false
-	end
-
-	return true
-end
-
-return F
+main.add_feature ( 'http://jabber.org/protocol/ibb' )
 
 -- vim: se ts=4: --
--- a/examples/iq.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-
--- CORE, IQ
-
--- library
-
-local lm = require 'lm'
-
---
-
-local F = { }
-
-function F.send ( conn, to, smtype, data, success, fail )
-	data.mtype = 'iq-' .. smtype
-	data.to    = to
-	conn:send ( lm.message.create ( data ),
-		function ( conn, mess )
-			local mtype, smtype = mess:type ()
-			if smtype == 'result' then
-				success ( mess )
-			elseif smtype == 'error' then
-				fail ( mess:child( 'error' ):child():name (), mess ) -- FIXME
-			else
-				fail ( mess:xml (), mess )
-				return false
-			end
-			return true
-		end )
-end
-
-return F
-
--- vim: se ts=4: --
--- a/examples/iq_register.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-
--- IN-BAND REGISTRATION (XEP-0077)
-
--- library
-
-local iq         = require 'iq'
-local x_data     = require 'x_data'
-local form_field = require 'form_field'
-
---
-
-local F = { }
-local M = { }
-M.__index = M
-
-function F.new ( args )
-	local form = {
-		xmlns        = 'jabber:iq:register',
-		ftype        = args.type or 'form',
-		registered   = args.registered,
-		-- XXX title
-		instructions = args.instructions,
-		x_data       = args.x_data,
-		f            = { },
-	}
-	setmetatable ( form, M )
-	return form
-end
-
-function F.parse ( query )
-	local instructions = query:child ( 'instructions' )
-	if instructions then
-		instructions = instructions:value ()
-	end
-	
-	local registered
-	if query:child ( 'registered' ) then
-		registered = true
-	end
-
-	local x = query:child ( 'x' )
-	if x and x:attribute ( 'xmlns' ) == 'jabber:x:data' then
-		return F.new { type = 'form', registered = registered, instructions = instructions, x_data = x_data.parse ( x ) }
-	end
-
-	local form = F.new { type = 'form', registered = registered, instructions = instructions }
-
-	local field = query:child ()
-	while field do
-		local name  = field:name ()
-		if name ~= 'instructions' and name ~= 'registered' then
-			form:add ( name, { type = 'text-single', value = field:value () or '' } )
-		end
-		field = field:next ()
-	end
-
-	return form
-end
-
-function M.type ( form )
-	return form.ftype
-end
-
-function M.desc ( form )
-	if form.x_data then
-		return form.x_data:desc ()
-	else
-		return form.title, form.instructions
-	end
-end
-
-function M.format ( form, root, format_as )
-	local ft = format_as or form:type ()
-
-	if form.x_data then
-		root.query = form.x_data:format ( { xmlns = 'jabber:iq:register' }, ft )
-	else
-		root.query = { xmlns = 'jabber:iq:register' }
-		for index, field in form:fields () do
-			root.query[field:name ()] = field:value ()
-		end
-	end
-
-	if ft == 'form' then
-		local title, instructions = form:desc ()
-		if instructions then
-			root.query.instructions = { instructions }
-		end
-		if form.registered then
-			root.query.registered = { }
-		end
-	end
-
-	return root
-end
-
-function M.fields ( form )
-	if form.x_data then
-		return form.x_data:fields ()
-	else
-		return ipairs ( form.f )
-	end
-end
-
-function M.add ( form, name, fld )
-	if form.x_data then
-		return form.x_data:add ( name, fld )
-	else
-		fld.var   = name
-		fld.index = #form.f + 1
-		local obj = form_field.new ( fld )
-		table.insert ( form.f, obj )
-		form.f[name] = obj
-		return obj
-	end
-end
-
-function M.field ( form, name )
-	if form.x_data then
-		return form.x_data:field ( name )
-	else
-		return form.f[name]
-	end
-end
-
-function F.register ( conn, to, success, fail )
-	iq.send ( conn, to, 'get',
-		{
-			query = { xmlns = 'jabber:iq:register' },
-		},
-		function ( mess )
-			local query = mess:child ( 'query' )
-			if query and query:attribute ( 'xmlns' ) == 'jabber:iq:register' then
-				success ( F.parse ( query ),
-					function ( form, success, fail )
-						iq.send ( conn, to, 'set', form:format ( form, { }, 'submit' ), success , fail )
-					end,
-					function ( form, success, fail )
-						success ()
-					end )
-			end
-		end, fail )
-end
-
-function F.unregister ( conn, to, success, fail )
-	iq.send ( conn, to, 'set',
-		{
-			query = { xmlns = 'jabber:iq:register',
-				remove = { },
-			},
-		},
-		function ( mess )
-			success ()
-		end,
-		function ( mesg, mess )
-			local query = mess:child ( 'query' )
-			if query and query:attribute ( 'xmlns' ) == 'jabber:iq:register' then
-				success ( F.parse ( query ),
-					function ( form, success, fail )
-						iq.send ( conn, to, 'set', form:format ( form, { }, 'submit' ), success, fail )
-					end,
-					function ( form, success, fail )
-						success ()
-					end )
-			else
-				fail ( mesg )
-			end
-		end )
-end
-
-return F
-
--- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/activity.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,28 @@
+
+-- USER ACTIVITY (XEP-0108)
+
+-- library
+
+local pep    = require 'lm.pep'
+
+-- public
+
+local F = { }
+
+function F.publish ( conn, success, fail, general, specific, message )
+	local item = { xmlns = 'http://jabber.org/protocol/activity' }
+	if general then
+		item[general] = { }
+		if specific then
+			item[general][specific] = { }
+		end
+	end
+	if message then
+		item.text = { message }
+	end
+	pep.publish ( conn, 'http://jabber.org/protocol/activity', { activity = item }, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/attention.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,48 @@
+
+-- ATTENTION (XEP-0224)
+
+-- library
+
+local lm = require 'lm'
+
+--
+
+local O = {
+	handler =
+		function ( mesg )
+		end,
+}
+
+local F = { }
+
+function F.send ( conn, to, message )
+	local body = nil
+	if message then
+		body = { message }
+	end
+	conn:send (
+		lm.message.create { mtype = 'message-headline', to = to,
+			attention = { xmlns = 'urn:xmpp:attention:0' },
+			body = body,
+		} )
+end
+
+function F.handler ( handler )
+	O.handler = handler
+end
+
+function F.message_handler ( conn, mess )
+	local a = mess:child ( 'attention' )
+	if a and a:attribute ( 'xmlns' ) == 'urn:xmpp:attention:0' then
+		local body = mess:child ( 'body' )
+		if body then
+			body = body:value ()
+		end
+		O.handler ( body )
+	end
+	return false
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/avatar.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,59 @@
+
+-- USER AVATAR (XEP-0084)
+
+-- library
+
+local sha1   = require 'sha1'
+local base64 = require 'base64'
+local lm     = require 'lm'
+local pep    = require 'lm.pep'
+local pubsub = require 'lm.pubsub'
+
+--
+
+local F = { }
+
+-- TODO 'temporary disabling'
+--          however I cannot see a method to enable it back without republishing avatar :(
+--          this requires client to know, what is published on the server now
+--          maybe we can do that by requesting item without payload from server
+--      pubsub metadata notification handler
+
+function F.publish ( conn, data, success, fail, height, width )
+	local id = sha1.digest ( data )
+	pep.publish ( conn, 'urn:xmpp:avatar:data',
+		{ id = id,
+			data = { xmlns = 'urn:xmpp:avatar:data',
+				base64.encode ( data ),
+			},
+		},
+		function ()
+			pep.publish ( conn, 'urn:xmpp:avatar:metadata',
+				{ id = id,
+					info = { bytes = data:len (), id = id, type = 'image/png', height = height, width = width },
+				}, success, fail )
+		end, fail )
+end
+
+function F.publish_url ( conn, url, iid, size, id, mtype, success, fail, height, width )
+	pep.publish ( conn, 'urn:xmpp:avatar:metadata',
+		{ id = iid,
+			info = { bytes = size, id = id, type = mtype, url = url, height = height, width = width },
+		}, success, fail )
+end
+
+function F.get ( conn, from, id, success, fail )
+	pubsub.retrieve ( conn, from, 'urn:xmpp:avatar:data',
+		function ( from, node, item )
+			local data = item:child ( 'data' )
+			if data then
+				success ( data:value () )
+			else
+				-- XXX
+			end
+		end, fail, nil, { id } )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/disco.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,58 @@
+
+-- SERVICE DISCOVERY (XEP-0030)
+
+-- TODO handler
+
+-- library
+
+local iq = require 'lm.iq'
+
+--
+
+local F = { }
+
+function F.items ( conn, to, success, fail, node )
+	iq.send ( conn, to, 'get',
+		{
+			query = { xmlns = 'http://jabber.org/protocol/disco#items', node = node }
+		},
+		function ( mess )
+			local item  = mess:child( 'query' ):child ()
+			local items = { }
+			while item do
+				if item:name () == 'item' then
+					table.insert ( items, { jid = item:attribute ( 'jid' ), node = item:attribute ( 'node' ), name = item:attribute ( 'name' ) } )
+				end
+				item = item:next ()
+			end
+			success ( items )
+		end,
+		fail )
+end
+
+function F.info ( conn, to, success, fail )
+	iq.send ( conn, to, 'get',
+		{
+			query = { xmlns='http://jabber.org/protocol/disco#info' }
+		},
+		function ( mess )
+			local identities = { }
+			local features   = { }
+			local item       = mess:child( 'query' ):child ()
+			while item do
+				local name  = item:name ()
+				if name == 'identity' then
+					table.insert ( identities, { category = item:attribute ( 'category' ), type = item:attribute ( 'type' ), name = item:attribute ( 'name' ) } )
+				elseif name == 'feature' then
+					table.insert ( features, item:attribute ( 'var' ) )
+				end
+				item = item:next ()
+			end
+			success ( identities, features )
+		end,
+		fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/evil.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,58 @@
+
+-- MALICIOUS STANZAS (XEP-0076)
+
+-- library
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+
+--
+
+local O = {
+	handler =
+		function ( mess )
+			return false
+		end,
+}
+
+local F = { }
+
+function F.handler ( handler )
+	O.handler = handler
+end
+
+function F.message ( conn, to, mtype, message )
+	conn:send ( lm.message.create { mtype = 'message-' .. mtype, to = to,
+			body = { message },
+			evil = { xmlns = 'http://jabber.org/protocol/evil' },
+		} )
+end
+
+function F.presence ( conn, to, status, message )
+	local mtype = 'presence-available'
+	if status == 'unavailable' then
+		mtype = 'presence-unavailable'
+		status = ''
+	end
+	conn:send ( lm.message.create { mtype = mtype, from = conn:jid (), to = to,
+			show   = { status },
+			status = { message },
+			evil   = { xmlns = 'http://jabber.org/protocol/evil' },
+		} )
+end
+
+function F.iq ( conn, to, action, contents, success, fail )
+	contents.evil = { xmlns = 'http://jabber.org/protocol/evil' }
+	iq.send ( conn, to, action, contents, success, fail )
+end
+
+function F.stanza_handler ( conn, mess )
+	local e = mess:child ( 'evil' )
+	if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/evil' then
+		return O.handler ( mess )
+	end
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/form_field.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,99 @@
+
+-- DATA FORM FIELD
+
+local F = { }
+local M = { }
+M.__index = M
+
+-- field.new      -- create new field
+-- field:index    -- get index
+-- field:type     -- get type
+-- field:name     -- get var
+-- field:desc     -- get label, desc
+-- field:value    -- get/set value
+-- field:values   -- get values
+-- field:clear    -- clear value
+-- field:options  -- get options
+-- field:required -- get required
+
+function F.new ( args )
+	local obj = {
+		t = args.type,
+		l = args.label,
+		d = args.desc,
+		o = args.options,  -- XXX
+		v = args.value,    -- XXX
+		r = args.required,
+		n = args.var,
+		i = args.index,    -- XXX
+	}
+	setmetatable ( obj, M )
+	return obj
+end
+
+function M.index ( obj )
+	return obj.i
+end
+
+function M.type ( obj )
+	return obj.t
+end
+
+function M.name ( obj )
+	return obj.n
+end
+
+function M.desc ( obj )
+	return obj.l, obj.d
+end
+
+function M.options ( obj )
+	return pairs ( obj.o )
+end
+
+function M.value ( obj, value )
+	local ftype = obj.t
+	if value == nil then
+		-- XXX
+		return obj.v
+	else
+		if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
+			table.insert ( obj.v, value )
+		else
+			obj.v = value
+		end
+	end
+end
+
+function M.values ( obj )
+	local ftype = obj.t
+	if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
+		return ipairs ( obj.v )
+	else
+		return
+			function ( arg )
+				if not arg then
+					return obj.v
+				else
+					return nil
+				end
+			end, nil
+	end
+end
+
+function M.clear ( obj )
+	local ftype = obj.t
+	if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' then
+		obj.v = { }
+	else
+		obj.v = ''
+	end
+end
+
+function M.required ( obj )
+	return obj.r
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/geoloc.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,24 @@
+
+-- USER LOCATION (XEP-0080)
+
+-- library
+
+local pep = require 'lm.pep'
+
+--
+
+local F = { }
+
+function F.publish ( conn, success, fail, data )
+	local sdata = { xmlns = 'http://jabber.org/protocol/geoloc' }
+	if data then
+		for key, value in pairs ( data ) do
+			sdata[key] = { value }
+		end
+	end
+	pep.publish ( conn, 'http://jabber.org/protocol/geoloc', { geoloc = sdata }, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/ibb.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,188 @@
+
+-- IN-BAND BYTESTREAMS (XEP-0047)
+
+-- TODO bidirectionality
+--        thus, on stream accept we can add our sid to incoming files structure,
+--        as if we received and accepted incoming request.
+--      message stanzas
+
+-- library
+
+local lm     = require 'lm'
+local iq     = require 'lm.iq'
+local base64 = require 'base64'
+
+--
+
+local F = { }
+local M = { }
+M.__index = M
+local O = {
+	handler =
+		function ( accept, reject )
+			reject ()
+		end,
+}
+
+function F.new ( conn, to, bs, sid )
+	local obj = {
+		conn = conn,
+		to   = to,
+		sbs  = bs,
+		bs   = math.floor ( bs * 3 / 4 ),
+		sid  = sid,
+		seq  = 0,
+	}
+	setmetatable ( obj, M )
+	return obj
+end
+
+function M.open ( obj, success, fail )
+	iq.send ( obj.conn, obj.to, 'set',
+		{
+			open = { sid = obj.sid, ['block-size'] = obj.sbs, xmlns = 'http://jabber.org/protocol/ibb' }
+		}, success, fail )
+end
+
+function M.send ( obj, data, success, fail )
+	if data and data ~= '' then
+		local start = 0
+		while start < data:len () do
+			local chunk = base64.encode ( data:sub ( start, obj.bs ) )
+			local cseq  = obj.seq -- local instance
+			iq.send ( obj.conn, obj.to, 'set',
+				{
+					data = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb', seq = cseq,
+						chunk,
+					},
+				},
+				function ()
+					success ( cseq )
+				end,
+				function ( mesg )
+					noerr = false
+					fail ( mesg )
+				end )
+			start   = start + obj.bs
+			obj.seq = obj.seq + 1
+		end
+	end
+end
+
+function M.close ( obj, success, fail )
+	iq.send ( obj.conn, obj.to, 'set',
+		{
+			close = { sid = obj.sid, xmlns = 'http://jabber.org/protocol/ibb' },
+		}, success, fail )
+end
+
+function F.handler ( handler )
+	O.handler = handler
+end
+
+local ibb_files = {}
+
+function F.iq_handler ( conn, mess )
+	local mtype, smtype = mess:type ()
+	if smtype ~= 'set' then
+		return false
+	end
+
+	local child = mess:child ()
+	if not child or child:attribute ( 'xmlns' ) ~= 'http://jabber.org/protocol/ibb' then
+		return false
+	end
+
+	local id     = mess:attribute ( 'id' )
+	local from   = mess:attribute ( 'from' )
+	local action = child:name ()
+	local sid    = child:attribute ( 'sid' )
+
+	if action == 'open' then
+		if not ibb_files[sid] then
+			O.handler ( from,
+				function ( success, fail )
+					ibb_files[sid] = { from = from, success = success, fail = fail }
+					conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+				end,
+				function ()
+					conn:send (
+						lm.message.create { to = from, mtype = 'iq-error', id = id,
+							error = { code = '405', type = 'cancel',
+								['not-allowed'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+							},
+						} )
+				end )
+		else
+			conn:send (
+				lm.message.create { to = from, mtype = 'iq-error', id = id,
+					error = { code = '409', type = 'cancel',
+						conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+					},
+				} )
+		end
+	elseif action == 'data' then
+		local seq = child:attribute ( 'seq' )
+		if ibb_files[sid] and from == ibb_files[sid].from and not ibb_files[sid][tonumber(seq)+1] then
+			local data = child:value ()
+			conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+			ibb_files[sid][tonumber(seq)+1] = data
+-- XXX		ibb_files[sid].success ( seq )
+		else
+			if ibb_files[sid] then
+				ibb_files[sid].fail ( 'conflict' )
+				ibb_files[sid] = nil -- invalidate session
+				conn:send (
+					lm.message.create { to = from, mtype = 'iq-error', id = id,
+						error = { code = '409', type = 'cancel',
+							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+						},
+					} )
+			else
+				conn:send (
+					lm.message.create { to = from, mtype = 'iq-error', id = id,
+						error = { code = '404', type = 'cancel', -- XXX: check
+							['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+						},
+					} )
+			end
+		end
+	elseif action == 'close' then
+		if ibb_files[sid] and from == ibb_files[sid].from then
+			local data = ''
+			for seq, chunk in ipairs ( ibb_files[sid] ) do
+				data = data .. chunk
+			end
+			local decoded = base64.decode ( data )
+			ibb_files[sid].success ( decoded )
+			conn:send ( lm.message.create { to = from, mtype = 'iq-result', id = id } )
+			ibb_files[sid] = nil
+		else
+			if ibb_files[sid] then
+				ibb_files[sid].fail ( 'conflict' )
+				ibb_files[sid] = nil -- invalidate session
+				conn:send (
+					lm.message.create { to = from, mtype = 'iq-error', id = id,
+						error = { code = '409', type = 'cancel',
+							conflict = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+						},
+					} )
+			else
+				conn:send (
+					lm.message.create { to = from, mtype = 'iq-error', id = id,
+						error = { code = '404', type = 'cancel', -- XXX: check
+							['item-not-found'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+						},
+					} )
+			end
+		end
+	else
+		return false
+	end
+
+	return true
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/iq.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,32 @@
+
+-- CORE, IQ
+
+-- library
+
+local lm = require 'lm'
+
+--
+
+local F = { }
+
+function F.send ( conn, to, smtype, data, success, fail )
+	data.mtype = 'iq-' .. smtype
+	data.to    = to
+	conn:send ( lm.message.create ( data ),
+		function ( conn, mess )
+			local mtype, smtype = mess:type ()
+			if smtype == 'result' then
+				success ( mess )
+			elseif smtype == 'error' then
+				fail ( mess:child( 'error' ):child():name (), mess ) -- FIXME
+			else
+				fail ( mess:xml (), mess )
+				return false
+			end
+			return true
+		end )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/iq_register.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,173 @@
+
+-- IN-BAND REGISTRATION (XEP-0077)
+
+-- library
+
+local iq         = require 'lm.iq'
+local x_data     = require 'lm.x_data'
+local form_field = require 'lm.form_field'
+
+--
+
+local F = { }
+local M = { }
+M.__index = M
+
+function F.new ( args )
+	local form = {
+		xmlns        = 'jabber:iq:register',
+		ftype        = args.type or 'form',
+		registered   = args.registered,
+		-- XXX title
+		instructions = args.instructions,
+		x_data       = args.x_data,
+		f            = { },
+	}
+	setmetatable ( form, M )
+	return form
+end
+
+function F.parse ( query )
+	local instructions = query:child ( 'instructions' )
+	if instructions then
+		instructions = instructions:value ()
+	end
+	
+	local registered
+	if query:child ( 'registered' ) then
+		registered = true
+	end
+
+	local x = query:child ( 'x' )
+	if x and x:attribute ( 'xmlns' ) == 'jabber:x:data' then
+		return F.new { type = 'form', registered = registered, instructions = instructions, x_data = x_data.parse ( x ) }
+	end
+
+	local form = F.new { type = 'form', registered = registered, instructions = instructions }
+
+	local field = query:child ()
+	while field do
+		local name  = field:name ()
+		if name ~= 'instructions' and name ~= 'registered' then
+			form:add ( name, { type = 'text-single', value = field:value () or '' } )
+		end
+		field = field:next ()
+	end
+
+	return form
+end
+
+function M.type ( form )
+	return form.ftype
+end
+
+function M.desc ( form )
+	if form.x_data then
+		return form.x_data:desc ()
+	else
+		return form.title, form.instructions
+	end
+end
+
+function M.format ( form, root, format_as )
+	local ft = format_as or form:type ()
+
+	if form.x_data then
+		root.query = form.x_data:format ( { xmlns = 'jabber:iq:register' }, ft )
+	else
+		root.query = { xmlns = 'jabber:iq:register' }
+		for index, field in form:fields () do
+			root.query[field:name ()] = field:value ()
+		end
+	end
+
+	if ft == 'form' then
+		local title, instructions = form:desc ()
+		if instructions then
+			root.query.instructions = { instructions }
+		end
+		if form.registered then
+			root.query.registered = { }
+		end
+	end
+
+	return root
+end
+
+function M.fields ( form )
+	if form.x_data then
+		return form.x_data:fields ()
+	else
+		return ipairs ( form.f )
+	end
+end
+
+function M.add ( form, name, fld )
+	if form.x_data then
+		return form.x_data:add ( name, fld )
+	else
+		fld.var   = name
+		fld.index = #form.f + 1
+		local obj = form_field.new ( fld )
+		table.insert ( form.f, obj )
+		form.f[name] = obj
+		return obj
+	end
+end
+
+function M.field ( form, name )
+	if form.x_data then
+		return form.x_data:field ( name )
+	else
+		return form.f[name]
+	end
+end
+
+function F.register ( conn, to, success, fail )
+	iq.send ( conn, to, 'get',
+		{
+			query = { xmlns = 'jabber:iq:register' },
+		},
+		function ( mess )
+			local query = mess:child ( 'query' )
+			if query and query:attribute ( 'xmlns' ) == 'jabber:iq:register' then
+				success ( F.parse ( query ),
+					function ( form, success, fail )
+						iq.send ( conn, to, 'set', form:format ( form, { }, 'submit' ), success , fail )
+					end,
+					function ( form, success, fail )
+						success ()
+					end )
+			end
+		end, fail )
+end
+
+function F.unregister ( conn, to, success, fail )
+	iq.send ( conn, to, 'set',
+		{
+			query = { xmlns = 'jabber:iq:register',
+				remove = { },
+			},
+		},
+		function ( mess )
+			success ()
+		end,
+		function ( mesg, mess )
+			local query = mess:child ( 'query' )
+			if query and query:attribute ( 'xmlns' ) == 'jabber:iq:register' then
+				success ( F.parse ( query ),
+					function ( form, success, fail )
+						iq.send ( conn, to, 'set', form:format ( form, { }, 'submit' ), success, fail )
+					end,
+					function ( form, success, fail )
+						success ()
+					end )
+			else
+				fail ( mesg )
+			end
+		end )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/mood.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,25 @@
+
+-- USER MOOD (XEP-0107)
+
+-- library
+
+local pep = require 'lm.pep'
+
+--
+
+local F = { }
+
+function F.publish ( conn, success, fail, mood, message )
+	local item = { xmlns = 'http://jabber.org/protocol/mood' }
+	if mood then
+		item[mood] = { }
+	end
+	if message then
+		item.text = { message }
+	end
+	pep.publish ( conn, 'http://jabber.org/protocol/mood', { mood = item }, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/oob.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,87 @@
+
+-- OUT OF BAND DATA (XEP-0066)
+
+-- library
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+
+--
+
+local O = {
+	handler =
+		function ( from, url, desc, success, fail )
+			fail ()
+		end,
+}
+
+local F = { }
+
+function F.send ( conn, to, url, success, fail, desc )
+	if desc then
+		desc = { desc }
+	end
+	iq.send ( conn, to, 'set',
+		{
+			query = { xmlns = 'jabber:iq:oob',
+				url  = { url },
+				desc = desc,
+			},
+		}, success, fail )
+end
+
+function F.handler ( handler )
+	O.handler = handler
+end
+
+function F.iq_handler ( conn, mess )
+	local mtype, smtype = mess:type ()
+	if smtype == 'set' then
+		local query = mess:child ( 'query' )
+		if query and query:attribute ( 'xmlns' ) == 'jabber:iq:oob' then
+			local from = mess:attribute ( 'from' )
+			local url  = query:child( 'url' ):value ()
+			local desc = query:child( 'desc' )
+			if desc then
+				desc = desc:value ()
+			end
+			O.handler ( from, url, desc,
+				function ()
+					conn:send ( lm.message.create { mtype = 'iq-result', to = from, id = mess:attribute ( 'id' ) } )
+				end,
+				function () -- XXX distinguish download error and reject
+					conn:send (
+						lm.message.create { mype = 'iq-error', to = from, id = mess:attribute ( 'id' ),
+							-- XXX must we include query here?
+							error = { code = '406', type = 'modify',
+								['not-acceptable'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
+							},
+						} )
+				end )
+			return true
+		end
+	end
+	return false
+end
+
+function F.message_handler ( conn, mess )
+	local x = mess:child ( 'x' )
+	if x and x:attribute ( 'xmlns' ) == 'jabber:x:oob' then
+		local from = mess:attribute ( 'from' )
+		local url  = x:child( 'url' ):value ()
+		local desc = x:child( 'desc' )
+		if desc then
+			desc = desc:value ()
+		end
+		O.handler ( from, url, desc,
+			function ()
+			end,
+			function ()
+			end )
+	end
+	return false
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/pep.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,39 @@
+
+-- PERSONAL EVENTING PROTOCOL (XEP-0163)
+
+-- library
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+
+--
+
+local F = { }
+
+function F.publish ( conn, node, item, success, fail )
+--	local bjid = conn:jid():gsub ( '/.*', '' )
+--	item.id = 'current'
+	iq.send ( conn, nil, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				publish = { node = node,
+					item = item,
+				},
+			},
+--[[
+			configure = {
+				x = {
+					field = {{ type = "hidden", var = 'FORM_TYPE',
+						value = { 'http://jabber.org/protocol/pubsub#node_config' },
+					},{ var = "pubsub#access_model",
+						value = { 'presence' },
+					}},
+				},
+			},
+--]]
+		}, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/ping.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,34 @@
+
+-- XMPP PING (XEP-0199)
+
+-- library
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+
+--
+
+local F = { }
+
+function F.send ( conn, to, success, fail )
+	iq.send ( conn, to, 'get',
+		{
+			ping = { xmlns = 'urn:xmpp:ping' },
+		}, success, fail )
+end
+
+function F.iq_handler ( conn, mess )
+	local mtype, smtype = mess:type ()
+	if smtype == 'get' then
+		local p = mess:child ( 'ping' )
+		if p and p:attribute ( 'xmlns' ) == 'urn:xmpp:ping' then
+			conn:send ( lm.message.create { mtype = 'iq-result', to = mess:attribute ( 'from' ), id = mess:attribute ( 'id' ) } )
+			return true
+		end
+	end
+	return false
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/privacy.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,133 @@
+
+local lm = require 'lm'
+local iq = require 'lm.iq'
+
+local F = { }
+local O = {
+	handler =
+		function ( name )
+		end,
+}
+
+function F.handler ( handler )
+	O.handler = handler
+end
+
+function F.lists ( conn, success, fail )
+	iq.send ( conn, nil, 'get',
+		{
+			query = { xmlns = 'jabber:iq:privacy' },
+		},
+		function ( mess )
+			local ret  = { }
+			local item = mess:child( 'query' ):child ()
+			while item do
+				local tag  = item:name ()
+				local name = item:attribute ( 'name' )
+				if tag == 'list' then
+					table.insert ( ret, name )
+				elseif tag == 'active' or tag == 'default' then
+					ret[tag] = name
+				end
+				item = item:next ()
+			end
+			success ( ret )
+		end, fail )
+end
+
+function F.list ( conn, name, success, fail )
+	iq.send ( conn, nil, 'get',
+		{
+			query = { xmlns = 'jabber:iq:privacy',
+				list = { name = name },
+			},
+		},
+		function ( mess )
+			local ret  = { }
+			local item = mess:path( 'query', 'list' ):child ()
+			while item do
+				local q = {
+					type   = item:attribute ( 'type' ),
+					value  = item:attribute ( 'value' ),
+					action = item:attribute ( 'action' ),
+					order  = item:attribute ( 'order' ),
+				}
+				local stanza = item:child ()
+				if stanza then
+					q.stanzas = { }
+					while stanza do
+						table.insert ( q.stanzas, stanza:name () )
+						stanza = stanza:next ()
+					end
+				end
+				item = item:next ()
+			end
+			success ( ret )
+		end, fail )
+end
+
+-- name may be nil
+function F.active ( conn, name, success, fail )
+	iq.send ( conn, nil, 'set',
+		{
+			query = { xmlns = 'jabber:iq:privacy',
+				active = { name = name },
+			},
+		}, success, fail )
+end
+
+-- name may be nil
+function F.default ( conn, name, success, fail )
+	iq.send ( conn, nil, 'set',
+		{
+			query = { xmlns = 'jabber:iq:privacy',
+				default = { name = name },
+			},
+		}, success, fail )
+end
+
+function F.set ( conn, name, list, success, fail )
+	local items = { }
+	local order = 1
+	for i, item in ipairs ( list ) do
+		if list.order then
+			order = list.order
+		else
+			order = order + 1
+		end
+		local q = {
+			type   = list.type,
+			value  = list.value,
+			action = list.action,
+			order  = order,
+		}
+		if list.stanzas then
+			for j, stanza in ipairs ( list.stanzas ) do
+				q[stanza] = { }
+			end
+		end
+		table.insert ( items, q )
+	end
+	iq.send ( conn, nil, 'set',
+		{
+			query = { xmlns = 'jabber:iq:privacy',
+				list = { name = name,
+					item = items,
+				},
+			},
+		}, success, fail )
+end
+
+function F.iq_handler ( conn, mess )
+	local query = mess:child ( 'query' )
+	if query and query:attribute ( 'xmlns' ) == 'jabber:iq:privacy' then
+		conn:send ( lm.message.create { mtype = 'iq-result', id = mess:attribute ( 'id' ) } )
+		O.handler ( query:child( 'list' ):attribute ( 'name' ) )
+		return true
+	end
+	return false
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/pubsub.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,222 @@
+
+-- PUBLISH-SUBSCRIBE (BEP-0060)
+
+-- library
+
+local iq     = require 'lm.iq'
+local x_data = require 'lm.x_data'
+
+--
+
+local O = {
+	handlers = { },
+}
+
+local F = { }
+
+function F.handler ( xmlns, handler )
+	O.handlers[xmlns] = handler
+end
+
+function F.message_handler ( conn, mess )
+	local e = mess:child ( 'event' )
+	if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/pubsub#event' then
+		local is = e:child ( 'items' )
+		if is then
+			local from = mess:attribute ( 'from' )
+			local node = is:attribute ( 'node' )
+			local item = is:child ()
+			while item do
+				local id = item:attribute ( 'id' )
+				local n  = item:child ()
+				while n do
+					local xmlns = n:attribute ( 'xmlns' )
+					if O.handlers[xmlns] then
+						O.handlers[xmlns] ( from, node, n, id )
+					end
+					n = n:next ()
+				end
+				item = item:next ()
+			end
+			return true
+		end
+	end
+	return false
+end
+
+-- SUBSCRIBER USE CASES
+
+function F.subscribe ( conn, to, node, success, fail )
+	local jid = conn:jid():gsub ( '/.*', '' )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				subscribe = { node = node, jid = jid },
+			},
+		},
+		function ( mess )
+			local s = mess:path ( 'pubsub', 'subscription' )
+			if s then
+				success ( s:attribute ( 'subid' ) )
+			else
+				success ()
+			end
+		end,
+		fail )
+end
+
+function F.unsubscribe ( conn, to, node, success, fail )
+	local jid = conn:jid():gsub ( '/.*', '' )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				unsubscribe = { node = node, jid = jid },
+			},
+		}, success, fail )
+end
+
+-- I found no servers with subscription options support thus it is not implemented.
+
+-- untested :(
+function F.retrieve ( conn, to, node, success, fail, max, ids )
+	local items = { node = node, max_items = max }
+	if ids then
+		items.item = { }
+		for k, id in pairs ( ids ) do
+			table.insert ( items.item, { id = id } )
+		end
+	end
+	iq.send ( conn, to, 'get',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				items = items,
+			},
+		},
+		function ( mess )
+			local items = mess:path ( 'pubsub', 'items' )
+			if items then
+				local from = mess:attribute ( 'from' )
+				local node = items:attribute ( 'node' )
+				local item = items:child ()
+				while item do
+					success ( from, node, item ) -- XXX use registered xmlns handlers for that?
+					item = item:next ()
+				end
+			else
+				-- XXX
+			end
+		end, fail )
+end
+
+-- OWNER USE CASES
+
+-- node may be nil
+function F.create_node ( conn, to, node, success, fail )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
+				create = { node = node },
+			},
+		},
+		function ( mess )
+			if node then
+				success ()
+			else
+				local create = mess:path ( 'pubsub', 'create' )
+				if create then
+					success ( create:attribute ( 'node' ) )
+				else
+					success ()
+				end
+			end
+		end, fail )
+end
+
+function F.delete_node ( conn, to, node, success, fail )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+				delete = { node = node },
+			},
+		}, success, fail )
+end
+
+function F.purge_node ( conn, to, node, success, fail )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+				purge = { node = node },
+			},
+		}, success, fail )
+end
+
+function F.configure_node ( conn, to, node, success, fail )
+	iq.send ( conn, to, 'get',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+				configure = { node = node },
+			},
+		},
+		function ( mess )
+			local x = mess:path ( 'pubsub', 'configure', 'x' )
+			if x then
+				success ( x_data.parse ( x ),
+					function ( form, success, fail )
+						iq.send ( conn, to, 'set',
+							{
+								pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+									configure = form:format ( { node = node }, 'submit' ),
+								},
+							}, success, fail )
+					end,
+					function ( form, success, fail )
+						iq.send ( conn, to, 'set',
+							{
+								pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+									configure = form:format ( { node = node }, 'cancel' ),
+								},
+							}, success, fail )
+					end )
+			else
+				fail ( mess:xml () ) -- XXX
+			end
+		end, fail )
+end
+
+function F.list_subscriptions ( conn, to, node, success, fail )
+	iq.send ( conn, to, 'get',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+				subscriptions = { node = node },
+			},
+		},
+		function ( mess )
+			local s = mess:path ( 'pubsub', 'subscriptions' )
+			if s then
+				local sub = s:child ()
+				local ret = { }
+				while sub do
+					table.insert ( ret, { jid = sub:attribute ( 'jid' ), subscription = sub:attribute ( 'subscription' ), subid = sub:attribute ( 'subid' ) } )
+					sub = sub:next ()
+				end
+				success ( ret )
+			else
+				fail ( mess:xml () ) -- XXX
+			end
+		end, fail )
+end
+
+function F.modify_subscription ( conn, to, node, jid, state, success, fail, id )
+	iq.send ( conn, to, 'set',
+		{
+			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
+				subscriptions = { node = node,
+					subscription = { jid = jid, subscription = state, id = id },
+				},
+			},
+		}, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/remote.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,64 @@
+
+-- REMOTE CONTROLLING CLIENTS (XEP-0146)
+
+-- library
+
+local iq     = require 'lm.iq'
+local x_data = require 'lm.x_data'
+local disco  = require 'lm.disco'
+
+--
+
+local F = { }
+
+function F.list ( conn, to, success, fail )
+	disco.items ( conn, to, success, fail, 'http://jabber.org/protocol/commands' )
+end
+
+function F.command ( conn, to, command, success, fail )
+	iq.send ( conn, to, 'set',
+		{
+			command = { xmlns = 'http://jabber.org/protocol/commands', action = 'execute', node = command },
+		},
+		function ( mess )
+			local c = mess:child ( 'command' )
+			if c then
+				local status = c:attribute ( 'status' )
+				if status == 'completed' then
+					success ()
+				else
+					local x = c:child ( 'x' )
+					if x then
+						local sid = c:attribute ( 'sessionid' )
+						success ( x_data.parse ( x ),
+							function ( form, success, fail )
+								iq.send ( conn, to, 'set',
+									{
+										command = form:format ( { xmlns = 'http://jabber.org/protocol/commands', node = command, sessionid = sid }, 'submit' ),
+									},
+									function ( mess )
+										local c = mess:child ( 'command' )
+										if c and c:attribute ( 'status' ) == 'completed' then
+											success ()
+										else
+											fail ( mess:xml () ) -- XXX more forms? results?
+										end
+									end, fail )
+							end,
+							function ( form, success, fail )
+								iq.send ( conn, to, 'set',
+									{
+										command = form:format ( { xmlns = 'http://jabber.org/protocol/commands', node = command, sessionid = sid }, 'cancel' ),
+									}, success, fail )
+							end )
+					else
+						fail ( mess:xml () ) -- XXX
+					end
+				end
+			end
+		end, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/tune.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,24 @@
+
+-- USER TUNE (XEP-0118)
+
+-- library
+
+local pep = require 'lm.pep'
+
+--
+
+local F = { }
+
+function F.publish ( conn, node, success, fail, data )
+	local item = { xmlns = 'http://jabber.org/protocol/tune' }
+	if data then
+		for key, value in pairs ( data ) do
+			item[key] = { value }
+		end
+	end
+	pep.publish ( conn, 'http://jabber.org/protocol/tune', { tune = item }, success, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/vcard.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,108 @@
+
+-- VCARD-TEMP (XEP-0054)
+
+-- library
+
+local iq         = require 'lm.iq'
+local form_field = require 'lm.form_field'
+
+--
+
+local F = { }
+local M = { }
+M.__index = M
+
+function F.new ( args )
+	local form = {
+		xmlns        = 'vcard-temp',
+		ftype        = args.type or 'form',
+		title        = args.title,
+		instructions = args.instructions,
+		f            = { },
+	}
+	setmetatable ( form, M )
+	return form
+end
+
+local function vcard_parse ( node, path, form )
+	local item = node:child ()
+	if item then
+		while item do
+			vcard_parse ( item, ( path and path .. '/' or '' ) .. item:name (), form )
+			item = item:next ()
+		end
+	elseif path then
+		form:add ( path, { type = 'text-single', value = node:value () } )
+	end
+end
+
+function F.parse ( card )
+	local form = F.new { type = 'form' }
+	vcard_parse ( card, nil, form )
+	return form
+end
+
+function M.type ( form )
+	return form.ftype
+end
+
+function M.desc ( form )
+	return form.title, form.instructions
+end
+
+function M.format ( form, root, format_as )
+	root.vCard = { xmlns = 'vcard-temp' }
+	for k, field in form:fields () do
+		local el = root.vCard
+		for k in field:name():gmatch ( '[^/]+' ) do
+			if not el[k] then
+				el[k] = { }
+			end
+			el = el[k]
+		end
+		el[1] = field:value ()
+	end
+	return root
+end
+
+function M.fields ( form )
+	return ipairs ( form.f )
+end
+
+function M.add ( form, name, fld )
+	fld.var   = name
+	fld.index = #form.f + 1
+	local obj = form_field.new ( fld )
+	table.insert ( form.f, obj )
+	form.f[name] = obj
+	return obj
+end
+
+function M.field ( form, name )
+	return form.f[name]
+end
+
+function F.retrieve ( conn, from, success, fail )
+	iq.send ( conn, from, 'get',
+		{
+			vCard = { xmlns = 'vcard-temp' },
+		},
+		function ( mess )
+			local card = mess:child ( 'vCard' )
+			if card and card:attribute ( 'xmlns' ) == 'vcard-temp' then
+				success ( F.parse ( mess:child ( 'vCard' ) ),
+					function ( form, success, fail )
+						iq.send ( conn, from, 'set', form:format ( form, { }, 'submit' ), success, fail )
+					end,
+					function ( form, success, fail )
+						success ()
+					end )
+			else
+				fail ( mess:xml () ) -- XXX
+			end
+		end, fail )
+end
+
+return F
+
+-- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/lm/x_data.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,226 @@
+
+-- DATA FORMS (XEP-0004)
+
+-- library
+
+local form_field = require 'lm.form_field'
+
+--
+
+local F = { }
+local M = { }
+M.__index = M
+
+-- form.new       -- new form
+-- form.parse     -- parse existing form -- according to form type!
+-- form:type      -- get type
+-- form:desc      -- get title, instructions
+-- form:format    -- format form -- according to form type!
+-- form:fields    -- iterator, returns field objects
+-- form:add       -- create field
+-- form:field     -- get field
+-- ?? form:del    -- delete field
+-- field.new      -- create field
+-- field:index    -- get index
+-- field:type     -- get type
+-- field:name     -- get var
+-- field:desc     -- get label, desc
+-- field:value    -- get/set value
+-- field:values   -- get values
+-- field:clear    -- clear value
+-- field:options  -- get options
+-- field:required -- get required
+
+-- form    may have 'fixed' fields and should be ordered
+-- submit  must not have 'fixed' fields and may be not ordered
+-- cancel  must not have anything
+-- result  should not have 'fixed', may be not ordered and can contain reported/item... multiple instances.
+
+function F.new ( args )
+	local form  = {
+		xmlns        = 'jabber:x:data',
+		ftype        = args.type or 'form',
+		title        = args.title,
+		instructions = args.instructions,
+		f            = { },
+	}
+	setmetatable ( form, M )
+	return form
+end
+
+function F.parse ( x )
+	local title = x:child ( 'title' )
+	if title then
+		title = title:value ()
+	end
+
+	local instructions = x:child ( 'instructions' )
+	if instructions then
+		instructions = instructions:value ()
+	end
+
+	local f = F.new { type = x:attribute ( 'type' ), title = title, instructions = instructions }
+
+	local field = x:child ()
+	while field do
+		local name = field:name ()
+		if name == 'field' then
+			local ftype = field:attribute ( 'type' ) or 'text-single'
+			local var   = field:attribute ( 'var' )
+			local r = {
+				type  = ftype,
+				label = field:attribute ( 'label' ),
+			}
+
+			local desc = field:child ( 'desc' )
+			if desc then
+				r.desc = desc:value ()
+			end
+
+			if field:child ( 'required' ) then
+				r.required = true
+			end
+
+			if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'list-single' then
+				local values  = { }
+				local options = { }
+
+				local item = field:child ()
+				while item do
+					local name = item:name ()
+					if name == 'value' then
+						table.insert ( values, item:value () )
+					elseif name == 'option' then
+						options[item:child( 'value' ):value ()] = item:attribute ( 'label' ) or ''
+					end
+					item = item:next ()
+				end
+
+				if ftype == 'list-single' then
+					local value = field:child ( 'value' )
+					if value then
+						r.value = value:value ()
+					end
+				else
+					r.value = values
+				end
+
+				if ftype == 'list-multi' or ftype == 'list-single' then
+					r.options = options
+				end
+			else
+				local value = field:child ( 'value' )
+				if value then
+					r.value = value:value ()
+				end
+			end
+
+			f:add ( var, r )
+		end
+
+		field = field:next ()
+	end
+
+	return f
+end
+
+function M.type ( form )
+	return form.ftype
+end
+
+function M.desc ( form )
+	return form.title, form.instructions
+end
+
+function M.format ( form, root, format_as )
+	local ft = format_as or form:type ()
+
+	root.x = { xmlns = 'jabber:x:data', type = ft }
+
+	if ft == 'cancel' then
+		return root
+	end
+
+	if ft == 'form' then
+		local title, instructions = form:desc ()
+		if title then
+			root.x.title = { title }
+		end
+		if instructions then
+			root.x.instructions = { instructions }
+		end
+	end
+
+	local fields = { }
+	for i, field in form:fields () do
+		local ftype = field:type ()
+
+		local options, label, desc, required
+		if ft == 'form' then
+			label, desc = field:desc ()
+			if desc then
+				desc = { desc }
+			end
+
+			if field:required () then
+				required = { }
+			end
+		
+			for option, label in field:options () do
+				table.insert ( options, { label = label, value = { option } } )
+			end
+		end
+
+		local value
+		if ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'jid-multi' then
+			if ft == 'form' then
+				for option, label in field:options () do
+					table.insert ( options, { label = label, value = { option } } )
+				end
+			end
+
+			value = { }
+			for j, v in field:values () do
+				table.insert ( value, { v } )
+			end
+		else
+			value = { field:value () }
+		end
+
+		if ftype ~= 'fixed' or ft == 'form' then
+			table.insert ( fields, {
+					type   = ftype,
+					var    = field:name (),
+					label  = label,
+					desc   = desc,
+					value  = value,
+					option = options
+				} )
+		end
+	end
+	root.x.field = fields
+
+	return root
+end
+
+function M.fields ( form )
+	return ipairs ( form.f )
+end
+
+function M.add ( form, var, fld )
+	fld.var   = var
+	fld.index = #form.f + 1 -- XXX
+	local obj = form_field.new ( fld )
+	table.insert ( form.f, obj )
+	form.f[var] = obj
+	return obj
+end
+
+function M.field ( form, var )
+	-- works well on indices
+	return form.f[var]
+end
+
+return F
+
+-- vim: se ts=4: --
--- a/examples/mc_activity.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-
-local lm       = require 'lm'
-local activity = require 'activity'
-local pubsub   = require 'pubsub'
-
-pubsub.handler ( 'http://jabber.org/protocol/activity',
-	function ( from, node, data )
-		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
-			return true
-		end
-		local item = data:child ()
-		local activity, desc
-		while item do
-			if item:name () == 'text' then
-				desc = item:value ()
-			else
-				activity = item:name ()
-				local subitem = item:child ()
-				if subitem then
-					-- here we can check for non-standard subactivity elements,
-					-- add subactivity child elements handling
-					activity = ("%s: %s"):format ( activity, subitem:name () )
-				end
-			end
-			item = item:next ()
-		end
-		if activity then
-			main.print_info ( from, ("Now %s %s"):format ( activity, desc or '' ) )
-		else
-			main.print_info ( from, "Buddy hides his activity" )
-		end
-		return true
-	end )
-
-main.command ( 'activity',
-	function ( args )
-		local a, text = args[1], args[2]
-		local act, subact = a:match ( "(.-)%-(.+)" )
-		if not act then
-			act = a
-		end
-		activity.publish ( lm.connection.bless ( main.connection () ),
-			function ()
-				print ( 'Activity published' )
-			end,
-			function ( mesg )
-				print ( 'Error publishing activity: ' .. mesg )
-			end, act, subact, text )
-	end, true )
-
-commands_help['activity'] = "[activity[-specific_activity] [text]]\n\nPublishes your activity.\nNote, that for now it does not checks for activity validity, so, see xep0108 for valid activity values."
-
-main.add_feature ( 'http://jabber.org/protocol/activity+notify' )
-main.add_feature ( 'http://jabber.org/protocol/activity' )
-
--- vim: se ts=4: --
--- a/examples/mc_attention.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-
-local lm        = require 'lm'
-local attention = require 'attention'
-
-attention.handler (
-	function ( mesg )
-		local times = 0
-		main.timer ( 1,
-			function ()
-				if times < 6 then
-					main.beep ()
-					times = times + 1
-					return true
-				end
-				return false
-			end )
-	end )
-
-main.command ( 'attention',
-	function ( args )
-		local who
-		if args.t then
-			who = args.t
-		else
-			who = main.full_jid ()
-		end
-		attention.send ( lm.connection.bless ( main.connection () ), who, args[1] )
-	end, true, 'jid' )
-
-commands_help['attention'] = "[-t to] [message]\n\nTries to get buddy's attention."
-
-local attention_handler = lm.message_handler.new ( attention.message_handler )
-local attention_handler_registered = false
-
-hooks_d['hook-post-connect'].attention =
-	function ( args )
-		lm.connection.bless( main.connection () ):handler ( attention_handler, 'message', 'normal' )
-		attention_handler_registered = true
-		hooks_d['hook-post-connect'].attention = nil
-		hooks_d['hook-quit'].attention =
-			function ( args )
-				if attention_handler_registered then
-					lm.connection.bless( main.connection () ):handler ( attention_handler, 'message' )
-				end
-			end
-	end
-
-main.add_feature ( 'urn:xmpp:attention:0' )
-
--- vim: se ts=4: --
--- a/examples/mc_avatar.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-
-local lm     = require 'lm'
-local pubsub = require 'pubsub'
-local avatar = require 'avatar'
-
-pubsub.handler ( 'urn:xmpp:avatar:metadata',
-	function ( from, node, data, id )
-		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
-			return true
-		end
-		local item = data:child ()
-		while item do
-			main.print_info ( from, ('Avatar: %s [%s] %s bytes, %sx%s %s'):format (
-					item:attribute ( 'id' ),
-					item:attribute ( 'type' ),
-					item:attribute ( 'bytes' ),
-					item:attribute ( 'width' ) or '?',
-					item:attribute ( 'height' ) or '?',
-					item:attribute ( 'url' ) or '' ) )
-			item = item:next ()
-		end
-	end )
-
-main.command ( 'avatar',
-	function ( args )
-		local action = args[1]
-		if action == 'get' then
-			local who
-			if args.t then
-				who = args.t
-			else
-				who = main.current_buddy ()
-			end
-			avatar.get ( lm.connection.bless ( main.connection () ), who, args[2],
-				function ( data )
-					local h = io.open ( args[3], 'w' )
-					if h then
-						h:write ( data )
-						h:close ()
-						main.print_info ( who, 'Avatar saved to ' .. args[3] )
-					else
-						print ( 'Cannot open file for writing ' .. args[3] )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Error obtaining avatar: ' .. mesg )
-				end )
-		else
-			local file = action
-			if action == 'set' then
-				file = args[2]
-			end
-			local h = io.open ( file )
-			if h then
-				data = h:read ( '*a' )
-				h:close ()
-				avatar.publish ( lm.connection.bless ( main.connection () ), data,
-					function ()
-						print ( 'Avatar published' )
-					end,
-					function ( mesg )
-						print ( 'Avatar publishing error: ' .. mesg )
-					end )
-			else
-				print ( 'Cannot open file ' .. file )
-			end
-		end
-	end, true, 'file' )
-
-commands_help['avatar'] = '[-t jid] get id filename | [set] filename\n\nGet action tries to get from server avatar with specified id and save it to \'filename\'.\nSet action publishes avatar to server. File must be a PNG image.'
-
-main.add_feature ( 'urn:xmpp:avatar:metadata+notify' )
-
--- vim: se ts=4: --
--- a/examples/mc_disco.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-
-local lm    = require 'lm'
-local disco = require 'disco'
-
-main.command ( 'disco',
-	function ( args )
-		local who
-		local conn = lm.connection.bless ( main.connection () )
-		if args.t then
-			who = args.t
-		else
-			who = main.full_jid ()
-		end
-		if args[1] == 'items' then
-			local node = args[2]
-			disco.items ( conn, who,
-				function ( items )
-					local text = ''
-					for index, item in ipairs ( items ) do
-						text = text .. ("\n    [%s (%s)] %s"):format ( item.jid or '', item.node or '', item.name or '' )
-					end
-					if text ~= '' then
-						main.print_info ( who, ("Items service discovery result for %s (%s):%s"):format ( who, node or '', text ) )
-					else
-						main.print_info ( who, ("No items in discovery result for %s (%s)"):format ( who, node or '' ) )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, ("Items service discovery for %s (%s) failed: %s"):format ( who, node or '', mesg ) )
-				end, node )
-		else
-			disco.info ( conn, who,
-				function ( identities, features )
-					main.print_info ( who, ("Service info discovery result for %s:"):format ( who ) )
-					local text = ''
-					for index, identity in ipairs ( identities ) do
-						text = text .. ("\n    [%s (%s)] %s"):format ( identity.category or '', identity.type or '', identity.name or '' )
-					end
-					if text ~= '' then
-						main.print_info ( who, "  Identities:" .. text )
-					else
-						main.print_info ( who, "  No identities" )
-					end
-					text = ''
-					for index, feature in ipairs ( features ) do
-						text = text .. ("\n    [%s]"):format ( feature or '' )
-					end
-					if text ~= '' then
-						main.print_info ( who, "  Features:" .. text )
-					else
-						main.print_info ( who, "  No features" )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, ("Info service discovery for %s failed: %s"):format ( who, mesg ) )
-				end )
-		end
-	end, true, 'jid' )
-
-commands_help['disco'] = "[-t target_jid] [info | items] [node]\n\nService discovery request.\nInfo is sent if omitted."
-
--- vim: se ts=4: --
--- a/examples/mc_evil.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-
-local lm   = require 'lm'
-local evil = require 'evil'
-
-evil.handler (
-	function ( mess )
-		local evillevel = tonumber(main.option ( 'lua_evil_sensibility' ))
-		local mtype, smtype = mess:type ()
-		if evillevel > 1 then
-			main.print_info ( mess:attribute ( 'from' ), 'Evil stanza of type ' .. mtype .. '.' .. smtype .. ' detected!' )
-		elseif evillevel > 0 then
-			print ( 'Tainted by evil stanza of type ' .. mtype .. '.' .. smtype .. ' from ' .. ( mess:attribute ( 'from' ) or '... unknown in black' ) )
-		end
-		return main.yesno ( main.option ( 'lua_filter_evil' ) )
-	end )
-
-local stat2xmpp = {
-	free     = 'chat',
-	online   = '',
-	away     = 'away',
-	dnd      = 'dnd',
-	notavail = 'xa',
-	offline  = 'unavailable',
-}
-
--- TODO improve interface, check if we sending right thing for offline
-main.command ( 'evil',
-	function ( args )
-		local conn = lm.connection.bless ( main.connection () )
-		if args[1] == 'status' then
-			local text = ''
-			for i, mesg in ipairs ( args ) do
-				if i > 2 then
-					text = text .. ' ' .. mesg
-				end
-			end
-			local st = stat2xmpp[args[2]]
-			if not st then
-				st = ''
-			end
-			evil.presence ( conn, args.t, st, text:sub ( 2 ) )
-		else
-			local text = ''
-			if args[1] == 'message' then
-				for i, mesg in ipairs ( args ) do
-					if i > 1 then
-						text = text .. ' ' .. mesg
-					end
-				end
-			else
-				for i, mesg in ipairs ( args ) do
-					text = text .. ' ' .. mesg
-				end
-			end
-			local mtype = 'chat'
-			if args.k then
-				mtype = args.k
-			end
-			local who
-			if args.t then
-				who = args.t
-			else
-				who = main.current_buddy ()
-			end
-			evil.message ( conn, who, mtype, text:sub ( 2 ) )
-		end
-	end, true )
-
-commands_help['evil'] = "[-t jid] [status stat [message] | [-k message_type] [message] message]\n\nSends evil message or presence.\nmessage_type may be chat, normal, headline.\nNote, that for now it will not change mcabber's status."
-
-local evil_handler = lm.message_handler.new ( evil.stanza_handler )
-local evil_handler_registered = false
-
-hooks_d['hook-post-connect'].evil =
-	function ( args )
-		lm.connection.bless( main.connection () ):handler ( evil_handler, 'iq',       'first' )
-		lm.connection.bless( main.connection () ):handler ( evil_handler, 'message',  'first' )
-		lm.connection.bless( main.connection () ):handler ( evil_handler, 'presence', 'first' )
-		evil_handler_registered = true
-		hooks_d['hook-post-connect'].evil = nil
-		hooks_d['hook-quit'].evil =
-			function ( args )
-				if evil_handler_registered then
-					lm.connection.bless( main.connection () ):handler ( evil_handler, 'iq'       )
-					lm.connection.bless( main.connection () ):handler ( evil_handler, 'message'  )
-					lm.connection.bless( main.connection () ):handler ( evil_handler, 'presence' )
-				end
-			end
-	end
-
-local char2xmpp = {
-	f = 'chat',
-	o = '',
-	a = 'away',
-	d = 'dnd',
-	n = 'xa',
-	_ = 'unavailable',
-}
-
--- hack, but working ;)
-hooks_d['hook-my-status-change'].evil =
-	function ( args )
-		if main.yesno ( main.option ( 'lua_evil_mode' ) ) then
-			evil.presence ( lm.connection.bless ( main.connection () ), nil, char2xmpp[args.new_status], args.message )
-		end
-	end
-
-main.add_feature ( 'http://jabber.org/protocol/evil' )
-
--- vim: se ts=4: --
--- a/examples/mc_forms.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-
-local form_cid = main.add_category { 'del', 'send' }
-local forms = { }
-
--- public
-function insert_form ( form, submit, reject )
-	table.insert ( forms, {
-			form   = form,
-			submit = submit,
-			reject = reject,
-		} )
-	main.add_completion ( form_cid, tostring(#forms) )
-	print ( "You have new form. Use /form " .. #forms .. " to fill and submit or cancel it." )
-	return #forms
-end
-
-main.command ( 'form',
-	function ( args )
-		local id, action = tonumber (args[1]), args[2]
-		if forms[id] then
-			local form = forms[id].form
-			if action == 'send' then
-				forms[id].submit ( form )
-			elseif action == 'reject' then
-				forms[id].reject ( form )
-			elseif action == 'del' then
-				main.del_completion ( form_cid, id )
-				forms[id] = nil
-			elseif action then
-				local fname, value
-				if action == 'set' then
-					fname = args[3]
-					value = args[4]
-				else
-					fname = action
-					value = args[3]
-				end
-				local field = form:field ( fname )
-				if field then
-					if value then
-						field:value ( value )
-					else
-						field:clear ()
-					end
-				else
-					-- XXX create?
-					print ( 'Field not found: ' .. fname )
-				end
-			else
-				local desc = 'Form ' .. id .. '\n - Type: ' .. form:type ()
-				local title, instructions = form:desc ()
-				if title then
-					desc = desc .. '\n - Title: ' .. title
-				end
-				if instructions then
-					desc = desc .. '\n - Instructions: ' .. instructions
-				end
-
-				local fields = 'Fields:'
-				for index, field in form:fields () do
-					local ftype = field:type ()
-
-					fields = fields .. '\n - '
-					
-					if field:required () then
-						fields = fields .. '* '
-					end
-
-					local label, descr = field:desc ()
-					if label then
-						fields = fields .. label .. ' (' .. ( field.var or '' ) .. ')'
-					else
-						fields = fields .. ( field:name () or '<no name>' )
-					end
-					fields = fields .. ' [' .. ftype .. ']'
-					if descr then
-						fields = fields .. ': ' .. descr
-					end
-
-					if ftype == 'list-single' or ftype == 'list-multi' then
-						fields = fields .. '\n   Options:'
-						for option, label in field:options () do
-							fields = fields .. '\n    * ' .. option .. ' - ' .. label
-						end
-					end
-					
-					if ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'jid-multi' then
-						fields = fields .. '\n   Values: '
-						for vin, value in field:values () do
-							fields = fields .. '\n    * ' .. value
-						end
-					else
-						fields = fields .. '\n   Value: ' .. ( field:value () or '' )
-					end
-				end
-				print ( desc .. '\n' .. fields )
-			end
-		else
-			local text = ''
-			for id, form in pairs ( forms ) do
-				local title = form.form:desc ()
-				text = text .. '\n - ' .. id .. ' ' .. ( title or 'Untitled' ) .. ' [' .. form.form:type () .. ']'
-			end
-			if text ~= '' then
-				print ( 'Forms list:' .. text )
-			else
-				print ( 'No forms' )
-			end
-		end
-	end, true, form_cid )
-
-commands_help['form'] = "[form_id [send | reject | [set] fieldname [value]]]\n\nWithout arguments prints form list.\nWith bare form id prints info on that form.\nWhen setting multivalue field, new values are added, not replacing previous.\nWithout value unsets field, multivalue fields lose all their values."
-
--- vim: se ts=4: --
--- a/examples/mc_geoloc.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-
-local lm     = require 'lm'
-local geoloc = require 'geoloc'
-local pubsub = require 'pubsub'
-
-pubsub.handler ( 'http://jabber.org/protocol/geoloc',
-	function ( from, node, data )
-		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
-			return true
-		end
-		local item = data:child ()
-		local text = ''
-		while item do
-			text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
-			item = item:next ()
-		end
-		if text ~= '' then
-			text = 'Now at:' .. text
-		else
-			text = 'Now in unknown location'
-		end
-		main.print_info ( from, text )
-		return true
-	end )
-
-main.command ( 'location',
-	function ( args )
-		geoloc.publish ( lm.connection.bless ( main.connection () ),
-			function ()
-				print ( 'Geolocation published' )
-			end,
-			function ( mesg )
-				print ( 'Error geolocation publishing: ' .. mesg )
-			end, args )
-	end, true )
-
-commands_help['location'] = "[-key value [-key value ...]]\n\nPublishes your current geolocation.\nValid keys are accuracy, alt, area, bearing, building, country, datum, description, error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp and uri, according to xep0080."
-
-main.add_feature ( 'http://jabber.org/protocol/geoloc+notify' )
-main.add_feature ( 'http://jabber.org/protocol/geoloc' )
-
--- vim: se ts=4: --
--- a/examples/mc_ibb.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-
-local lm  = require 'lm'
-local ibb = require 'ibb'
-
-local mc_incoming_files = { }
-
-ibb.handler (
-	function ( from, accept, reject )
-		local fid = #mc_incoming_files + 1
-		mc_incoming_files[fid] = {
-				from   = from,
-				accept =
-					function ( name )
-						mc_incoming_files[fid].name = name
-						accept (
-							function ( data )
-								local h = io.open ( mc_incoming_files[fid].name, 'w' )
-								if not h then
-									print ( 'Cannot open output file: ' .. mc_incoming_files[fid].name )
-									return
-								end
-								h:write ( data )
-								h:close ()
-								print ( 'Stream ' .. fid .. ' successfully saved to ' .. mc_incoming_files[fid].name )
-								mc_incoming_files[fid] = nil
-							end,
-							function ( mesg )
-								main.print_info ( from, 'Stream error: ' .. mesg )
-								mc_incoming_files[fid] = nil -- XXX
-							end )
-					end,
-				reject =
-					function ()
-						reject ()
-						print ( 'Stream ' .. fid .. ' rejected' )
-						mc_incoming_files[fid] = nil
-					end,
-		}
-		main.print_info ( from, from .. ' wants you to receive stream. Use /ibb [accept|reject] ' .. fid .. ' to process his request.' )
-	end )
-
-local ibb_sid = 0
-
-main.command ( 'ibb',
-	function ( args )
-		local action = args[1]
-		if action == 'send' then
-			local who
-			if args.t then
-				who = args.t
-			else
-				who = main.full_jid ()
-			end
-			local fname = args[2]
-			local conn  = lm.connection.bless ( main.connection () )
-			local sid   = ibb_sid
-			ibb_sid     = ibb_sid + 1
-			local stream = ibb.new ( conn, who, 4096, sid )
-			stream:open (
-				function ()
-					main.print_info ( who, 'Stream accepted' )
-					local noerr = true
-					local h     = io.open ( fname, 'r' )
-					if not h then
-						print ( 'Cannot open file ' .. fname )
-						return
-					end
-					local data = h:read ( '*a' ) -- In fact, it is better to read it in chunks :/
-					h:close ()
-					local fail =
-						function ( mesg )
-							noerr = false
-							main.print_info ( who, 'Stream error: ' .. mesg )
-						end
-					stream:send ( data,
-						function ( seq )
-							main.print_info ( who, 'Delivery notification of chunk #' .. seq )
-						end, fail )
-					if noerr then
-						stream:close (
-							function ()
-								main.print_info ( who, 'Stream finalizing notification' )
-							end, fail )
-					end
-					if noerr then
-						main.print_info ( who, 'Stream sent' )
-					else
-						main.print_info ( who, 'Stream error occured' )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Stream initiation error: ' .. mesg )
-				end )
-		elseif action == 'accept' then
-			local id = tonumber(args[2])
-			if mc_incoming_files[id] then
-				mc_incoming_files[id].accept ( args[3] )
-			end
-		elseif action == 'reject' then
-			local id = tonumber(args[2])
-			if mc_incoming_files[id] then
-				mc_incoming_files[id].reject ()
-			end
-		else
-			local text = ''
-			for sid, data in pairs ( mc_incoming_files ) do
-				text = text .. '\n' ..  sid .. ': ' .. data.from .. ' --> ' .. ( data.name or '?' )
-			end
-			if text ~= '' then
-				print ( 'List of incoming streams:' .. text )
-			else
-				print ( 'No streams' )
-			end
-		end
-	end, true, { "send", "accept", "reject" } )
-
-
-commands_help['ibb'] = "[[-t target_jid] send filename | accept sid filename | reject sid]\n\nRequests, accepts or rejects sending file via in-band bytestream."
-
-local ibb_handler            = lm.message_handler.new ( ibb.iq_handler )
-local ibb_handler_registered = false
-
-hooks_d['hook-post-connect'].ibb =
-	function ( args )
-		lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq', 'normal' )
-		ibb_handler_registered = true
-		hooks_d['hook-post-connect'].ibb = nil
-		hooks_d['hook-quit'].ibb =
-			function ( args )
-				if ibb_handler_registered then
-					lm.connection.bless( main.connection () ):handler ( ibb_handler, 'iq' )
-				end
-			end
-	end
-
-main.add_feature ( 'http://jabber.org/protocol/ibb' )
-
--- vim: se ts=4: --
--- a/examples/mc_mood.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-
-local lm     = require 'lm'
-local mood   = require 'mood'
-local pubsub = require 'pubsub'
-
-pubsub.handler ( 'http://jabber.org/protocol/mood',
-	function ( from, node, data )
-		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
-			return true
-		end
-		local item = data:child ()
-		local mood, desc
-		while item do
-			if item:name () == 'text' then
-				desc = item:value ()
-			else
-				mood = item:name ()
-				-- here we can add child elements handling (by namespace)
-			end
-			item = item:next ()
-		end
-		if mood then
-			main.print_info ( from, ("Buddy's mood now %s %s"):format ( mood, desc or '' ) )
-		else
-			main.print_info ( from, "Buddy hides his mood" )
-		end
-	end )
-
-main.command ( 'mood',
-	function ( args )
-		mood.publish ( lm.connection.bless ( main.connection () ),
-			function ()
-				print ( 'Mood published' )
-			end,
-			function ( mesg )
-				print ( 'Error publishing mood: ' .. mesg )
-			end, args[1], args[2] )
-	end, true )
-
-commands_help['mood'] = "[mood [message]]\n\nPublishes your mood.\nNote, that for now it does not checks for mood validity, so, see xep0107 for valid moods."
-
-main.add_feature ( 'http://jabber.org/protocol/mood+notify' )
-main.add_feature ( 'http://jabber.org/protocol/mood' )
-
--- vim: se ts=4: --
--- a/examples/mc_oob.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-
-local lm  = require 'lm'
-local oob = require 'oob'
-
-oob.handler (
-	function ( from, url, desc, success, fail )
-		if desc then
-			main.print_info ( from, 'Buddy wants you to download link: ' .. url .. ' (' .. desc .. ')' )
-		else
-			main.print_info ( from, 'Buddy wants you to download link: ' .. url )
-		end
-		success ()
-	end )
-
-main.command ( 'oob',
-	function ( args )
-		local who
-		if args.t then
-			who = args.t
-		else
-			who = main.full_jid ()
-		end
-		-- here we can run something external to put file on server and obtain link to it
-		oob.send ( lm.connection.bless ( main.connection () ), who, args[1],
-			function ()
-				main.print_info ( who, 'OOB link accepted' )
-			end,
-			function ( mesg )
-				main.print_info ( who, 'OOB link refused: ' .. mesg )
-			end, args[2] )
-	end, true )
-
-local oob_iq_handler = lm.message_handler.new ( oob.iq_handler )
-local oob_message_handler = lm.message_handler.new ( oob.message_handler )
-local oob_handler_registered = false
-
-hooks_d['hook-post-connect'].oob =
-	function ( args )
-		local conn = lm.connection.bless ( main.connection () )
-		conn:handler ( oob_iq_handler, 'iq', 'normal' )
-		conn:handler ( oob_message_handler, 'message', 'normal' )
-		conn:handler ( oob_message_handler, 'presence', 'normal' )
-		oob_handler_registered = true
-		hooks_d['hook-post-connect'].oob = nil
-		hooks_d['hook-quit'].oob =
-			function ( args )
-				if oob_handler_registered then
-					local conn = lm.connection.bless ( main.connection () )
-					conn:handler ( oob_iq_handler, 'iq' )
-					conn:handler ( oob_message_handler, 'message' )
-					conn:handler ( oob_message_handler, 'presence' )
-				end
-			end
-	end
-
-main.add_feature ( 'jabber:iq:oob' )
-main.add_feature ( 'jabber:x:oob' )
-
--- vim: se ts=4: --
--- a/examples/mc_ping.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-
-local lm   = require 'lm'
-local ping = require 'ping'
-
-main.command ( 'ping',
-	function ( args )
-		local who
-		if args[1] then
-			who = args[1]
-		else
-			who = main.full_jid ()
-		end
-		local time = os.time ()
-		ping.send ( lm.connection.bless ( main.connection () ), who,
-			function ()
-				main.print_info ( who, ('Pong: %d seconds'):format ( os.time () - time ) )
-			end,
-			function ( mesg )
-				main.print_info ( who, 'Ping failed: ' .. mesg )
-			end )
-	end, true, 'jid' )
-
---[[
-local ping_handler = lm.message_handler.new ( ping.iq_handler )
-local ping_handler_registered = false
-
-hooks_d['hook-post-connect'].ping =
-	function ( args )
-		lm.connection.bless( main.connection () ):handler ( ping_handler, 'iq', 'normal' )
-		ping_handler_registered = true
-		hooks_d['hook-post-connect'].ping = nil
-		hooks_d['hook-quit'].ping =
-			function ( args )
-				if ping_handler_registered then
-					lm.connection.bless( main.connection () ):handler ( ping_handler, 'iq' )
-				end
-			end
-	end
-
-main.add_feature ( 'urn:xmpp:ping' )
---]]
-
--- vim: se ts=4: --
--- a/examples/mc_pubsub.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-
-local lm     = require 'lm'
-local pubsub = require 'pubsub'
-
-main.command ( 'node',
-	function ( args )
-		local who, action, node = args.t, args[1], args[2]
-		local conn = lm.connection.bless ( main.connection () )
-		if not who then
-			who = main.current_buddy ()
-		end
-		if action == 'subscribe' then
-			pubsub.subscribe ( conn, who, node,
-				function ( id )
-					if id then
-						main.print_info ( who, 'Subscription succeeds with id ' .. id )
-					else
-						main.print_info ( who, 'Subscription successful' )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Subscription unsuccessful: ' .. mesg )
-				end )
-		elseif action == 'unsubscribe' then
-			pubsub.unsubscribe ( conn, who, node,
-				function ()
-					main.print_info ( who, 'Unubscription successful' )
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Unsubscription unsuccessful: ' .. mesg )
-				end )
-		elseif action == 'retrieve' or action == 'items' or action == 'get' then
-			pubsub.retrieve ( conn, who, node,
-				function ( from, node, item )
-					main.print_info ( who, 'Item from ' .. from .. ', node ' .. node .. ':\n' .. item:xml () ) 
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Retrieval failed: ' .. mesg )
-				end, args.m )
-		elseif action == 'create' or action == 'new' then
-			pubsub.create_node ( conn, who, node,
-				function ( node )
-					if node then
-						main.print_info ( who, 'Node ' .. node .. ' successfully created' )
-					else
-						main.print_info ( who, 'Node successfully created' )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Creation failed: ' .. mesg )
-				end )
-		elseif action == 'delete' or action == 'del' then
-			pubsub.delete_node ( conn, who, node,
-				function ()
-					main.print_info ( who, 'Node deleted' )
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Node deletion failed: ' .. mesg )
-				end )
-		elseif action == 'purge' or action == 'del_items' then
-			pubsub.purge_node ( conn, who, node,
-				function ()
-					main.print_info ( who, 'Node purged' )
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Node purge failed: ' .. mesg )
-				end )
-		elseif action:sub ( 1, 4 ) == 'conf' then
-			pubsub.configure_node ( conn, who, node,
-				function ( form, submit, reject )
-					insert_form ( form,
-						function ( form )
-							submit ( form,
-								function ()
-									main.print_info ( who, 'Node configuration completed' )
-								end,
-								function ( mesg )
-									main.print_info ( who, 'Node configuration failed: ' .. mesg )
-								end )
-						end,
-						function ( form )
-							reject ( form,
-								function ()
-									main.print_info ( who, 'Node configuration cancelled' )
-								end,
-								function ( mesg )
-									main.print_info ( who, 'Node configuration cancellation failed: ' .. mesg )
-								end )
-						end )
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Node configuration failed: ' .. mesg )
-				end )
-		elseif action == 'subscriptions' or action == 'subscribers' then
-			pubsub.list_subscriptions ( conn, who, node,
-				function ( s )
-					local text = ''
-					for i, v in ipairs ( s ) do
-						local subid = v.subid
-						if subid then
-							subid = '(id ' .. subid .. ')'
-						else
-							subid = ''
-						end
-						text = text .. ('\n- [%s] %s %s'):format ( v.subscription, v.jid, subid )
-					end
-					if text ~= '' then
-						main.print_info ( who, 'Node subscriptions:' .. text )
-					else
-						main.print_info ( who, 'No subscriptions' )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Node subscriptions retrieval failed: ' .. mesg )
-				end )
-		elseif action == 'subscription' or action == 'modify' then -- XXX
-			pubsub.modify_subscription ( conn, args.t or main.current_buddy (), node, args[3], args[4],
-				function ()
-					main.print_info ( who, 'Subscription modified' )
-				end,
-				function ( mesg )
-					main.print_info ( who, 'Subsrciption modification failed: ' .. mesg )
-				end, args[5] )
-		else
-			print ( 'Error: unknown action' )
-		end
-	end, true )
-
--- FIXME
-commands_help['node']           = "[-t jid] [-m max_items] action [node_name]\n\nAction can be subscribe, unsubscribe, retrieve (items, get), create (new), delete (del), purge (del_items), configure (conf*), subscriptions (subscribers), subscription (modify?)"
---[[
-commands_help['subscribe']      = "[-t jid] node_name\n\nSends pubsub subscription request to specified node of jid or current buddy."
-commands_help['unsubscribe']    = "[-t jid] node_name\n\nSends pubsub unsubscription request to specified node of jid or current buddy."
-commands_help['retrieve']       = "[-t jid] [-m max_items] node_name\n\nSends pubsub items retrieval request to specified node of jid or current buddy.\nNote, that we cannot know, how to deal with these itemss, so, raw xml will be printed as a result."
-commands_help['create_node']    = "[-t jid] [node_name]\n\nSends pubsub node creation request to specified node of jid or current buddy. If node name is not specified, server can generate unique id for it, if supported."
-commands_help['configure_node'] = "[-t jid] node_name\n\nSends pubsub node configuration request to specified node of jid or current buddy."
-commands_help['delete_node']    = "[-t jid] node_name\n\nSends pubsub node deletion request to specified node of jid or current buddy."
-commands_help['purge_node']     = "[-t jid] node_name\n\nSends pubsub node items purging request to specified node of jid or current buddy."
-commands_help['subscriptions']  = "[-t jid] node_name\n\nSends pubsub subscription list request to specified node of jid or current buddy."
-commands_help['subscription']   = "[-t jid] node_name subscriber_jid state [subscription_id]\n\nSends pubsub subscription modification request to change subscription state of 'subscriber_jid' to 'state'. Optional id is used when multiple subscriptions for one jid are available."
---]]
-
-local pubsub_handler = lm.message_handler.new ( pubsub.message_handler )
-
-local pubsub_handler_registered = false
-
-hooks_d['hook-post-connect'].pubsub =
-	function ( args )
-		lm.connection.bless( main.connection () ):handler ( pubsub_handler, 'message', 'normal' )
-		pubsub_handler_registered = true
-		hooks_d['hook-post-connect'].pubsub = nil
-		hooks_d['hook-quit'].pubsub =
-			function ( args )
-				if pubsub_handler_registered then
-					lm.connection.bless( main.connection () ):handler ( pubsub_handler, 'message' )
-					pubsub_handler_registered = false
-				end
-			end
-	end
-
--- vim: se ts=4: --
--- a/examples/mc_register.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-
-local lm          = require 'lm'
-local iq_register = require 'iq_register'
-
-main.command ( 'register',
-	function ( args )
-		local who
-		if args and args ~= '' then
-			who = args
-		else
-			who = main.full_jid ()
-		end
-		iq_register.register ( lm.connection.bless ( main.connection () ), who,
-			function ( form, submit, reject )
-				insert_form ( form,
-					function ( form )
-						submit ( form,
-							function ()
-								main.print_info ( who, 'Successfully registered' )
-							end,
-							function ( mesg )
-								main.print_info ( who, 'Registration failed: ' .. mesg )
-							end )
-					end,
-					function ( form )
-						reject ( form,
-							function ()
-								main.print_info ( who, 'Registration cancelled' )
-							end,
-							function ( mesg )
-								main.print_info ( who, 'Registration cancellation failed: ' .. mesg )
-							end )
-					end )
-			end,
-			function ( mesg )
-				main.print_info ( who, 'Registration failed: ' .. mesg )
-			end )
-	end, false, 'jid' )
-main.command ( 'cancel',
-	function ( args )
-		local who
-		if args and args ~= '' then
-			who = args
-		else
-			who = main.full_jid ()
-		end
-		iq_register.unregister ( lm.connection.bless ( main.connection () ), who,
-			function ( form, submit, reject )
-				if not form then
-					main.print_info ( who, 'Successfully unregistered' )
-				else
-					insert_form ( form,
-						function ( form )
-							submit ( form,
-								function ()
-									main.print_info ( who, 'Successfully unregistered' )
-								end,
-								function ( mesg )
-									main.print_info ( who, 'Unregistrering failed: ' .. mesg )
-								end )
-						end,
-						function ( form )
-							reject ( form,
-								function ()
-									main.print_info ( who, 'Unregistration cancelled' )
-								end,
-								function ( mesg )
-									main.print_info ( who, 'Unregistration cancellation failed: ' .. mesg )
-								end )
-						end )
-				end
-			end,
-			function ( mesg )
-				main.print_info ( who, 'Unregistering failed: ' .. mesg )
-			end )
-	end, false, 'jid' )
-
-commands_help['register'] = "[jid]\n\nSends registration request to jid (or current buddy). You, probably, then will need to fill and send some form."
-commands_help['cancel'] = "[jid]\n\nSends registration cancellation request to jid (or current buddy). May require a form filling."
-
--- vim: se ts=4: --
--- a/examples/mc_remote.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-
-local lm     = require 'lm'
-local remote = require 'remote'
-
-main.command ( 'remote',
-	function ( args )
-		local who
-		if args.t then
-			who = args.t
-		else
-			who = main.full_jid ()
-		end
-		local action = args[1]
-		local conn   = lm.connection.bless ( main.connection () )
-		if action then
-			remote.command ( conn, who, action,
-				function ( form, submit, reject )
-					if not form then
-						main.print_info ( who, ('Command %s completed'):format ( action ) )
-					else
-						insert_form ( form, -- XXX
-							function ( form )
-								submit ( form,
-									function ()
-										main.print_info ( who, ('Command %s completed'):format ( action ) )
-									end,
-									function ( mesg )
-										main.print_info ( who, ('Command %s execution failed: %s'):format ( action, mesg ) )
-									end )
-							end,
-							function ( form )
-								reject ( form,
-									function ()
-										main.print_info ( who, ('Command %s execution cancelled'):format ( action ) )
-									end,
-									function ( mesg )
-										main.print_info ( who, ('Command %s execution cancellation failed: %s'):format ( action, mesg ) )
-									end )
-							end )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, ('Command %s execution failed: %s'):format ( action, mesg ) )
-				end )
-		else
-			remote.list ( conn, who,
-				function ( items )
-					local text = ''
-					for index, item in ipairs ( items ) do
-						text = text .. '\n - ' .. item.node
-					end
-					if text ~= '' then
-						main.print_info ( who, 'Available commands:' .. text )
-					else
-						main.print_info ( who, 'No commands available.' )
-					end
-				end,
-				function ( mesg )
-					main.print_info ( who, ("Remote commands list for %s failed: %s"):format ( who, mesg ) )
-				end )
-		end
-	end, true, 'jid' )
-
-commands_help['remote'] = "[-t target_jid] [remote_command]\n\nPrints list of available remote command or requests execution of specified command."
-
--- vim: se ts=4: --
--- a/examples/mc_tune.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-
-local mpd    = require 'mpd'
-local lm     = require 'lm'
-local pubsub = require 'pubsub'
-
-local tune_enabled = false
-local mpd_pub_song = { }
-
-pubsub.handler ( 'http://jabber.org/protocol/tune',
-	function ( from, node, data )
-		local self = false
-		if from == lm.connection.bless ( main.connection () ):jid():gsub ( '/.*', '' ) then -- o_O
-			self         = true
-			mpd_pub_song = { }
-		end
-		local item = data:child ()
-		local text = ''
-		while item do
-			local name  = item:name ()
-			local value = item:value ()
-			if self then
-				mpd_pub_song[name] = value or ''
-			end
-			text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
-			item = item:next ()
-		end
-		if main.yesno ( main.option ( 'lua_pep_notification' ) ) then
-			if text ~= '' then
-				text = 'Now listening to:' .. text
-			else
-				text = 'Now not listening to anything'
-			end
-		end
-		main.print_info ( from, text )
-		return true
-	end )
-
-local function mpd_getstatus ()
-	local status = mpd.call_command { 'status' }
-	if not tune_enabled or ( status.state ~= 'play' and status.state ~= 'pause' ) then
-		for k, v in pairs ( mpd_pub_song ) do -- if there is anything published, publish nothing
-			return { }
-		end
-		return nil
-	end
-	
-	local song      = mpd.call_command { 'currentsong' }
-	local dir, file = song.file:match ( '(.+)/(.-)' )
-	-- populate according to currentsong fields: artist - artist, length - time, source - album, title - title, track - id, rating - ?, uri - ?
-	local ret = {
-		artist = song.artist or 'Unknown',
-		length = song.time,
-		source = song.album  or dir,
-		title  = song.title  or file,
-		track  = song.id,
-	}
-
-	if not song.time or song.time == '0' then -- XXX
-		ret.length = nil
-	end
-
-	local modified = false
-	for k, v in pairs ( ret ) do
-		if mpd_pub_song[k] ~= v then
-			modified = true
-			break
-		end
-	end
-	if not modified then
-		for k, v in pairs ( mpd_pub_song ) do
-			if ret[k] ~= v then
-				modified = true
-				break
-			end
-		end
-	end
-
-	if modified then
-		return ret
-	else
-		return nil
-	end
-end
-
-local function mpd_callback ()
-	local sdata = mpd_getstatus ()
-	if sdata then
-		tune.publish ( lm.connection.bless ( main.connection () ),
-			function ()
-			end,
-			function ( mesg )
-				print ( 'Error publishing tune: ' .. mesg )
-			end, sdata )
-	end
-	if tune_enabled then
-		return true
-	else
-		return false
-	end
-end
-
--- do not call it too fast, or you end up with many daemons at once
-local function enable_tune ( yn )
-	if yn == nil then
-		yn = true
-	end
-	if yn then
-		if not tune_enabled then
-			main.timer ( 15, mpd_callback )
-			tune_enabled = true
-			-- update status
-		end
-	else
-		if tune_enabled then
-			tune_enabled = false
-			-- update status
-		end
-	end
-end
-
-main.command ( 'tune',
-	function ( args )
-		local enable = main.yesno ( args )
-		if enable == nil then
-			if tune_enabled then
-				print ( "Tune notifications enabled" )
-			else
-				print ( "Tune notifications disabled" )
-			end
-		else
-			enable_tune ( enable )
-		end
-	end, false, 'yesno' )
-
-commands_help['tune'] = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables publishing of notifications about playing music in your player (currently only mpd is supported)."
-
-hooks_d['hook-post-connect'].tune =
-	function ( args )
-		if tune_enabled then
-			mpd_callback ()
-		end
-	end
-
-main.add_feature ( 'http://jabber.org/protocol/tune+notify' )
-main.add_feature ( 'http://jabber.org/protocol/tune' )
-
--- vim: se ts=4: --
--- a/examples/mc_vcard.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-
-local lm    = require 'lm'
-local vcard = require 'vcard'
-
-main.command ( 'vcard-temp',
-	function ( args )
-		vcard.retrieve ( lm.connection.bless ( main.connection () ), args[1],
-			function ( form, submit, reject )
-				insert_form ( form,
-					function ( form )
-						submit ( form,
-							function ()
-								print ( 'Vcard changed' )
-							end,
-							function ( mesg )
-								print ( 'Vcard changing error: ' .. mesg )
-							end )
-					end,
-					function ( form )
-						reject ( form,
-							function ()
-								print ( 'Vcard changing cancelled' )
-							end,
-							function ( mesg )
-								print ( 'Vcard changing cancellation error: ' .. mesg )
-							end )
-					end )
-			end,
-			function ( mesg )
-				print ( 'Vcard obtaining error: ' .. mesg )
-			end )
-	end, true, 'jid' )
-
--- vim: se ts=4: --
--- a/examples/mcabberrc.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/mcabberrc.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -257,70 +257,74 @@
 
 -- FORMS
 
-require 'mc_forms'
+require 'forms'
 
 -- DATA FORMS (XEP-0004)
 
+-- PRIVACY LISTS (XEP-0016)
+
+require 'privacy'
+
 -- SERVICE DISCOVERY (XEP-0030)
 
-require 'mc_disco'
+require 'disco'
 
 -- IN-BAND BYTESTREAMS (XEP-0047)
 
-require 'mc_ibb'
+require 'ibb'
 
 -- VCARD-TEMP (XEP-0054)
 
-require 'mc_vcard'
+require 'vcard'
 
 -- PUBLISH-SUBSRIBE (XEP-0060)
 
-require 'mc_pubsub'
+require 'pubsub'
 
 -- OUT OF BAND DATA (XEP-0066)
 
-require 'mc_oob'
+require 'oob'
 
 -- MALICIOUS STANZAS (XEP-0076)
 
-require 'mc_evil'
+require 'evil'
 
 -- IN-BAND REGISTRATION (XEP-0077)
 
-require 'mc_register'
+require 'register'
 
 -- USER LOCATION (XEP-0080)
 
-require 'mc_geoloc'
+require 'geoloc'
 
 -- USER AVATAR (XEP-0084)
 
-require 'mc_avatar'
+require 'avatar'
 
 -- USER MOOD (XEP-0107)
 
-require 'mc_mood'
+require 'mood'
 
 -- USER ACTIVITY (XEP-0108)
 
-require 'mc_activity'
+require 'activity'
 
 -- USER TUNE (XEP-0118)
 
-require 'mc_tune'
+require 'tune'
 
 -- REMOTE CONTROLLING CLIENTS (XEP-0146)
 
-require 'mc_remote'
+require 'remote'
 
 -- PERSONAL EVENTING PROTOCOL (XEP-0163)
 
 -- XMPP PING (XEP-0199)
 
-require 'mc_ping'
+require 'ping'
 
 -- ATTENTION (XEP-0224)
 
-require 'mc_attention'
+require 'attention'
 
 -- The End -- vim: se ts=4: --
--- a/examples/mood.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/mood.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,25 +1,45 @@
 
--- USER MOOD (XEP-0107)
-
--- library
-
-local pep = require 'pep'
-
---
-
-local F = { }
+local lm     = require 'lm'
+local mood   = require 'lm.mood'
+local pubsub = require 'lm.pubsub'
 
-function F.publish ( conn, success, fail, mood, message )
-	local item = { xmlns = 'http://jabber.org/protocol/mood' }
-	if mood then
-		item[mood] = { }
-	end
-	if message then
-		item.text = { message }
-	end
-	pep.publish ( conn, 'http://jabber.org/protocol/mood', { mood = item }, success, fail )
-end
+pubsub.handler ( 'http://jabber.org/protocol/mood',
+	function ( from, node, data )
+		if not main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+			return true
+		end
+		local item = data:child ()
+		local mood, desc
+		while item do
+			if item:name () == 'text' then
+				desc = item:value ()
+			else
+				mood = item:name ()
+				-- here we can add child elements handling (by namespace)
+			end
+			item = item:next ()
+		end
+		if mood then
+			main.print_info ( from, ("Buddy's mood now %s %s"):format ( mood, desc or '' ) )
+		else
+			main.print_info ( from, "Buddy hides his mood" )
+		end
+	end )
 
-return F
+main.command ( 'mood',
+	function ( args )
+		mood.publish ( lm.connection.bless ( main.connection () ),
+			function ()
+				print ( 'Mood published' )
+			end,
+			function ( mesg )
+				print ( 'Error publishing mood: ' .. mesg )
+			end, args[1], args[2] )
+	end, true )
+
+commands_help['mood'] = "[mood [message]]\n\nPublishes your mood.\nNote, that for now it does not checks for mood validity, so, see xep0107 for valid moods."
+
+main.add_feature ( 'http://jabber.org/protocol/mood+notify' )
+main.add_feature ( 'http://jabber.org/protocol/mood' )
 
 -- vim: se ts=4: --
--- a/examples/oob.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/oob.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,87 +1,59 @@
 
--- OUT OF BAND DATA (XEP-0066)
-
--- library
-
-local lm = require 'lm'
-local iq = require 'iq'
-
---
-
-local O = {
-	handler =
-		function ( from, url, desc, success, fail )
-			fail ()
-		end,
-}
+local lm  = require 'lm'
+local oob = require 'lm.oob'
 
-local F = { }
-
-function F.send ( conn, to, url, success, fail, desc )
-	if desc then
-		desc = { desc }
-	end
-	iq.send ( conn, to, 'set',
-		{
-			query = { xmlns = 'jabber:iq:oob',
-				url  = { url },
-				desc = desc,
-			},
-		}, success, fail )
-end
-
-function F.handler ( handler )
-	O.handler = handler
-end
+oob.handler (
+	function ( from, url, desc, success, fail )
+		if desc then
+			main.print_info ( from, 'Buddy wants you to download link: ' .. url .. ' (' .. desc .. ')' )
+		else
+			main.print_info ( from, 'Buddy wants you to download link: ' .. url )
+		end
+		success ()
+	end )
 
-function F.iq_handler ( conn, mess )
-	local mtype, smtype = mess:type ()
-	if smtype == 'set' then
-		local query = mess:child ( 'query' )
-		if query and query:attribute ( 'xmlns' ) == 'jabber:iq:oob' then
-			local from = mess:attribute ( 'from' )
-			local url  = query:child( 'url' ):value ()
-			local desc = query:child( 'desc' )
-			if desc then
-				desc = desc:value ()
+main.command ( 'oob',
+	function ( args )
+		local who
+		if args.t then
+			who = args.t
+		else
+			who = main.full_jid ()
+		end
+		-- here we can run something external to put file on server and obtain link to it
+		oob.send ( lm.connection.bless ( main.connection () ), who, args[1],
+			function ()
+				main.print_info ( who, 'OOB link accepted' )
+			end,
+			function ( mesg )
+				main.print_info ( who, 'OOB link refused: ' .. mesg )
+			end, args[2] )
+	end, true )
+
+local oob_iq_handler = lm.message_handler.new ( oob.iq_handler )
+local oob_message_handler = lm.message_handler.new ( oob.message_handler )
+local oob_handler_registered = false
+
+hooks_d['hook-post-connect'].oob =
+	function ( args )
+		local conn = lm.connection.bless ( main.connection () )
+		conn:handler ( oob_iq_handler, 'iq', 'normal' )
+		conn:handler ( oob_message_handler, 'message', 'normal' )
+		conn:handler ( oob_message_handler, 'presence', 'normal' )
+		oob_handler_registered = true
+		hooks_d['hook-post-connect'].oob = nil
+		hooks_d['hook-quit'].oob =
+			function ( args )
+				if oob_handler_registered then
+					local conn = lm.connection.bless ( main.connection () )
+					conn:handler ( oob_iq_handler, 'iq' )
+					conn:handler ( oob_message_handler, 'message' )
+					conn:handler ( oob_message_handler, 'presence' )
+				end
 			end
-			O.handler ( from, url, desc,
-				function ()
-					conn:send ( lm.message.create { mtype = 'iq-result', to = from, id = mess:attribute ( 'id' ) } )
-				end,
-				function () -- XXX distinguish download error and reject
-					conn:send (
-						lm.message.create { mype = 'iq-error', to = from, id = mess:attribute ( 'id' ),
-							-- XXX must we include query here?
-							error = { code = '406', type = 'modify',
-								['not-acceptable'] = { xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas' },
-							},
-						} )
-				end )
-			return true
-		end
 	end
-	return false
-end
 
-function F.message_handler ( conn, mess )
-	local x = mess:child ( 'x' )
-	if x and x:attribute ( 'xmlns' ) == 'jabber:x:oob' then
-		local from = mess:attribute ( 'from' )
-		local url  = x:child( 'url' ):value ()
-		local desc = x:child( 'desc' )
-		if desc then
-			desc = desc:value ()
-		end
-		O.handler ( from, url, desc,
-			function ()
-			end,
-			function ()
-			end )
-	end
-	return false
-end
-
-return F
+main.add_feature ( 'jabber:iq:oob' )
+main.add_feature ( 'jabber:x:oob' )
 
 -- vim: se ts=4: --
--- a/examples/pep.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-
--- PERSONAL EVENTING PROTOCOL (XEP-0163)
-
--- library
-
-local lm = require 'lm'
-local iq = require 'iq'
-
---
-
-local F = { }
-
-function F.publish ( conn, node, item, success, fail )
---	local bjid = conn:jid():gsub ( '/.*', '' )
---	item.id = 'current'
-	iq.send ( conn, nil, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
-				publish = { node = node,
-					item = item,
-				},
-			},
---[[
-			configure = {
-				x = {
-					field = {{ type = "hidden", var = 'FORM_TYPE',
-						value = { 'http://jabber.org/protocol/pubsub#node_config' },
-					},{ var = "pubsub#access_model",
-						value = { 'presence' },
-					}},
-				},
-			},
---]]
-		}, success, fail )
-end
-
-return F
-
--- vim: se ts=4: --
--- a/examples/ping.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/ping.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,34 +1,43 @@
 
--- XMPP PING (XEP-0199)
-
--- library
-
-local lm = require 'lm'
-local iq = require 'iq'
-
---
-
-local F = { }
+local lm   = require 'lm'
+local ping = require 'lm.ping'
 
-function F.send ( conn, to, success, fail )
-	iq.send ( conn, to, 'get',
-		{
-			ping = { xmlns = 'urn:xmpp:ping' },
-		}, success, fail )
-end
+main.command ( 'ping',
+	function ( args )
+		local who
+		if args[1] then
+			who = args[1]
+		else
+			who = main.full_jid ()
+		end
+		local time = os.time ()
+		ping.send ( lm.connection.bless ( main.connection () ), who,
+			function ()
+				main.print_info ( who, ('Pong: %d seconds'):format ( os.time () - time ) )
+			end,
+			function ( mesg )
+				main.print_info ( who, 'Ping failed: ' .. mesg )
+			end )
+	end, true, 'jid' )
 
-function F.iq_handler ( conn, mess )
-	local mtype, smtype = mess:type ()
-	if smtype == 'get' then
-		local p = mess:child ( 'ping' )
-		if p and p:attribute ( 'xmlns' ) == 'urn:xmpp:ping' then
-			conn:send ( lm.message.create { mtype = 'iq-result', to = mess:attribute ( 'from' ), id = mess:attribute ( 'id' ) } )
-			return true
-		end
+--[[
+local ping_handler = lm.message_handler.new ( ping.iq_handler )
+local ping_handler_registered = false
+
+hooks_d['hook-post-connect'].ping =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( ping_handler, 'iq', 'normal' )
+		ping_handler_registered = true
+		hooks_d['hook-post-connect'].ping = nil
+		hooks_d['hook-quit'].ping =
+			function ( args )
+				if ping_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( ping_handler, 'iq' )
+				end
+			end
 	end
-	return false
-end
 
-return F
+main.add_feature ( 'urn:xmpp:ping' )
+--]]
 
 -- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/privacy.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,98 @@
+
+local lm      = require 'lm'
+local privacy = require 'lm.privacy'
+
+privacy.handler (
+	function ( name )
+		print ( 'Privacy list changed: ' .. name )
+	end )
+
+main.command ( 'blocklist',
+	function ( args )
+		local action = args[1]
+		local conn   = lm.connection.bless ( main.connection () )
+		local who    = main.current_buddy ()
+		privacy.list ( conn, 'mcabber',
+			function ( list )
+				if action == 'add' then
+					for i, item in ipairs ( list ) do
+						item.order = nil
+					end
+					table.insert ( list, 1, { type = 'jid', value = args[2] or who, action = 'deny' } )
+				elseif action == 'del' then
+					for i, item in ipairs ( list ) do
+						if item.value == ( args[2] or who ) then
+							table.remove ( list, i )
+							break
+						end
+					end
+				else
+					local text = ''
+					for i, item in ipairs ( list ) do
+						text = text .. '\n - <' .. ( list.order or '?' ) .. '> ' .. ( list.value or '*' ) .. ' [' .. ( list.type or 'unspecified' ) .. '] ' .. ( list.action or 'unspecified' )
+					end
+					if text ~= '' then
+						print ( 'Blocking list: ' .. text )
+					end
+					return
+				end
+				privacy.set ( conn, 'mcabber', list,
+					function ()
+						print ( 'Blocking list saved' )
+					end,
+					function ( mesg )
+						print ( 'Error saving blocking list: ' .. mesg )
+					end )
+			end,
+			function ( mesg )
+				if action == 'add' then
+					privacy.set ( conn, 'mcabber',
+						{{ type = 'jid', value = args[2] or who, action = 'deny' },
+						 { action = 'allow' }},
+						function ()
+							print ( 'New blocking list created' )
+						end,
+						function ( message )
+							print ( 'Error obtaining/creatieng blocking list: ' .. mesg .. ' / ' .. message )
+						end )
+				else
+					print ( 'Cannot obtain privacy list from server: ' .. mesg )
+				end
+			end )
+	end, true )
+
+commands_help['blocklist'] = '[[add | del] [jid]]\n\nRetrieves list, adds or removes buddies to/from mcabber\'s server blocklist.\nAny stanzas from buddy in list are blocked by server.'
+
+local privacy_handler            = lm.message_handler.new ( privacy.iq_handler )
+local privacy_handler_registered = false
+
+hooks_d['hook-post-connect'].privacy =
+	function ( args )
+		privacy.active ( lm.connection.bless ( main.connection () ), 'mcabber',
+			function ()
+				print ( 'Server blocklist activated' )
+			end,
+			function ()
+				-- list may be absent, so, we will not pollute log with errors
+			end )
+		lm.connection.bless( main.connection () ):handler ( privacy_handler, 'iq', 'normal' )
+		privacy_handler_registered = true
+		hooks_d['hook-post-connect'].privacy =
+			function ( args )
+				privacy.active ( lm.connection.bless ( main.connection () ), 'mcabber',
+					function ()
+						print ( 'Server blocklist activated' )
+					end,
+					function ()
+						-- list may be absent, so, we will not pollute log with errors
+					end )
+			end
+		hooks_d['hook-quit'].privacy =
+			function ( args )
+				if privacy_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( privacy_handler, 'iq' )
+				end
+			end
+	end
+
+-- vim: se ts=4: --
--- a/examples/pubsub.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/pubsub.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,222 +1,161 @@
 
--- PUBLISH-SUBSCRIBE (BEP-0060)
-
--- library
-
-local iq     = require 'iq'
-local x_data = require 'x_data'
-
---
-
-local O = {
-	handlers = { },
-}
-
-local F = { }
-
-function F.handler ( xmlns, handler )
-	O.handlers[xmlns] = handler
-end
-
-function F.message_handler ( conn, mess )
-	local e = mess:child ( 'event' )
-	if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/pubsub#event' then
-		local is = e:child ( 'items' )
-		if is then
-			local from = mess:attribute ( 'from' )
-			local node = is:attribute ( 'node' )
-			local item = is:child ()
-			while item do
-				local id = item:attribute ( 'id' )
-				local n  = item:child ()
-				while n do
-					local xmlns = n:attribute ( 'xmlns' )
-					if O.handlers[xmlns] then
-						O.handlers[xmlns] ( from, node, n, id )
-					end
-					n = n:next ()
-				end
-				item = item:next ()
-			end
-			return true
-		end
-	end
-	return false
-end
-
--- SUBSCRIBER USE CASES
+local lm     = require 'lm'
+local pubsub = require 'lm.pubsub'
 
-function F.subscribe ( conn, to, node, success, fail )
-	local jid = conn:jid():gsub ( '/.*', '' )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
-				subscribe = { node = node, jid = jid },
-			},
-		},
-		function ( mess )
-			local s = mess:path ( 'pubsub', 'subscription' )
-			if s then
-				success ( s:attribute ( 'subid' ) )
-			else
-				success ()
-			end
-		end,
-		fail )
-end
-
-function F.unsubscribe ( conn, to, node, success, fail )
-	local jid = conn:jid():gsub ( '/.*', '' )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
-				unsubscribe = { node = node, jid = jid },
-			},
-		}, success, fail )
-end
-
--- I found no servers with subscription options support thus it is not implemented.
-
--- untested :(
-function F.retrieve ( conn, to, node, success, fail, max, ids )
-	local items = { node = node, max_items = max }
-	if ids then
-		items.item = { }
-		for k, id in pairs ( ids ) do
-			table.insert ( items.item, { id = id } )
+main.command ( 'node',
+	function ( args )
+		local who, action, node = args.t, args[1], args[2]
+		local conn = lm.connection.bless ( main.connection () )
+		if not who then
+			who = main.current_buddy ()
 		end
-	end
-	iq.send ( conn, to, 'get',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
-				items = items,
-			},
-		},
-		function ( mess )
-			local items = mess:path ( 'pubsub', 'items' )
-			if items then
-				local from = mess:attribute ( 'from' )
-				local node = items:attribute ( 'node' )
-				local item = items:child ()
-				while item do
-					success ( from, node, item ) -- XXX use registered xmlns handlers for that?
-					item = item:next ()
-				end
-			else
-				-- XXX
-			end
-		end, fail )
-end
+		if action == 'subscribe' then
+			pubsub.subscribe ( conn, who, node,
+				function ( id )
+					if id then
+						main.print_info ( who, 'Subscription succeeds with id ' .. id )
+					else
+						main.print_info ( who, 'Subscription successful' )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Subscription unsuccessful: ' .. mesg )
+				end )
+		elseif action == 'unsubscribe' then
+			pubsub.unsubscribe ( conn, who, node,
+				function ()
+					main.print_info ( who, 'Unubscription successful' )
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Unsubscription unsuccessful: ' .. mesg )
+				end )
+		elseif action == 'retrieve' or action == 'items' or action == 'get' then
+			pubsub.retrieve ( conn, who, node,
+				function ( from, node, item )
+					main.print_info ( who, 'Item from ' .. from .. ', node ' .. node .. ':\n' .. item:xml () ) 
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Retrieval failed: ' .. mesg )
+				end, args.m )
+		elseif action == 'create' or action == 'new' then
+			pubsub.create_node ( conn, who, node,
+				function ( node )
+					if node then
+						main.print_info ( who, 'Node ' .. node .. ' successfully created' )
+					else
+						main.print_info ( who, 'Node successfully created' )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Creation failed: ' .. mesg )
+				end )
+		elseif action == 'delete' or action == 'del' then
+			pubsub.delete_node ( conn, who, node,
+				function ()
+					main.print_info ( who, 'Node deleted' )
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Node deletion failed: ' .. mesg )
+				end )
+		elseif action == 'purge' or action == 'del_items' then
+			pubsub.purge_node ( conn, who, node,
+				function ()
+					main.print_info ( who, 'Node purged' )
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Node purge failed: ' .. mesg )
+				end )
+		elseif action:sub ( 1, 4 ) == 'conf' then
+			pubsub.configure_node ( conn, who, node,
+				function ( form, submit, reject )
+					insert_form ( form,
+						function ( form )
+							submit ( form,
+								function ()
+									main.print_info ( who, 'Node configuration completed' )
+								end,
+								function ( mesg )
+									main.print_info ( who, 'Node configuration failed: ' .. mesg )
+								end )
+						end,
+						function ( form )
+							reject ( form,
+								function ()
+									main.print_info ( who, 'Node configuration cancelled' )
+								end,
+								function ( mesg )
+									main.print_info ( who, 'Node configuration cancellation failed: ' .. mesg )
+								end )
+						end )
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Node configuration failed: ' .. mesg )
+				end )
+		elseif action == 'subscriptions' or action == 'subscribers' then
+			pubsub.list_subscriptions ( conn, who, node,
+				function ( s )
+					local text = ''
+					for i, v in ipairs ( s ) do
+						local subid = v.subid
+						if subid then
+							subid = '(id ' .. subid .. ')'
+						else
+							subid = ''
+						end
+						text = text .. ('\n- [%s] %s %s'):format ( v.subscription, v.jid, subid )
+					end
+					if text ~= '' then
+						main.print_info ( who, 'Node subscriptions:' .. text )
+					else
+						main.print_info ( who, 'No subscriptions' )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Node subscriptions retrieval failed: ' .. mesg )
+				end )
+		elseif action == 'subscription' or action == 'modify' then -- XXX
+			pubsub.modify_subscription ( conn, args.t or main.current_buddy (), node, args[3], args[4],
+				function ()
+					main.print_info ( who, 'Subscription modified' )
+				end,
+				function ( mesg )
+					main.print_info ( who, 'Subsrciption modification failed: ' .. mesg )
+				end, args[5] )
+		else
+			print ( 'Error: unknown action' )
+		end
+	end, true )
 
--- OWNER USE CASES
+-- FIXME
+commands_help['node']           = "[-t jid] [-m max_items] action [node_name]\n\nAction can be subscribe, unsubscribe, retrieve (items, get), create (new), delete (del), purge (del_items), configure (conf*), subscriptions (subscribers), subscription (modify?)"
+--[[
+commands_help['subscribe']      = "[-t jid] node_name\n\nSends pubsub subscription request to specified node of jid or current buddy."
+commands_help['unsubscribe']    = "[-t jid] node_name\n\nSends pubsub unsubscription request to specified node of jid or current buddy."
+commands_help['retrieve']       = "[-t jid] [-m max_items] node_name\n\nSends pubsub items retrieval request to specified node of jid or current buddy.\nNote, that we cannot know, how to deal with these itemss, so, raw xml will be printed as a result."
+commands_help['create_node']    = "[-t jid] [node_name]\n\nSends pubsub node creation request to specified node of jid or current buddy. If node name is not specified, server can generate unique id for it, if supported."
+commands_help['configure_node'] = "[-t jid] node_name\n\nSends pubsub node configuration request to specified node of jid or current buddy."
+commands_help['delete_node']    = "[-t jid] node_name\n\nSends pubsub node deletion request to specified node of jid or current buddy."
+commands_help['purge_node']     = "[-t jid] node_name\n\nSends pubsub node items purging request to specified node of jid or current buddy."
+commands_help['subscriptions']  = "[-t jid] node_name\n\nSends pubsub subscription list request to specified node of jid or current buddy."
+commands_help['subscription']   = "[-t jid] node_name subscriber_jid state [subscription_id]\n\nSends pubsub subscription modification request to change subscription state of 'subscriber_jid' to 'state'. Optional id is used when multiple subscriptions for one jid are available."
+--]]
 
--- node may be nil
-function F.create_node ( conn, to, node, success, fail )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
-				create = { node = node },
-			},
-		},
-		function ( mess )
-			if node then
-				success ()
-			else
-				local create = mess:path ( 'pubsub', 'create' )
-				if create then
-					success ( create:attribute ( 'node' ) )
-				else
-					success ()
+local pubsub_handler = lm.message_handler.new ( pubsub.message_handler )
+
+local pubsub_handler_registered = false
+
+hooks_d['hook-post-connect'].pubsub =
+	function ( args )
+		lm.connection.bless( main.connection () ):handler ( pubsub_handler, 'message', 'normal' )
+		pubsub_handler_registered = true
+		hooks_d['hook-post-connect'].pubsub = nil
+		hooks_d['hook-quit'].pubsub =
+			function ( args )
+				if pubsub_handler_registered then
+					lm.connection.bless( main.connection () ):handler ( pubsub_handler, 'message' )
+					pubsub_handler_registered = false
 				end
 			end
-		end, fail )
-end
-
-function F.delete_node ( conn, to, node, success, fail )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-				delete = { node = node },
-			},
-		}, success, fail )
-end
-
-function F.purge_node ( conn, to, node, success, fail )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-				purge = { node = node },
-			},
-		}, success, fail )
-end
-
-function F.configure_node ( conn, to, node, success, fail )
-	iq.send ( conn, to, 'get',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-				configure = { node = node },
-			},
-		},
-		function ( mess )
-			local x = mess:path ( 'pubsub', 'configure', 'x' )
-			if x then
-				success ( x_data.parse ( x ),
-					function ( form, success, fail )
-						iq.send ( conn, to, 'set',
-							{
-								pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-									configure = form:format ( { node = node }, 'submit' ),
-								},
-							}, success, fail )
-					end,
-					function ( form, success, fail )
-						iq.send ( conn, to, 'set',
-							{
-								pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-									configure = form:format ( { node = node }, 'cancel' ),
-								},
-							}, success, fail )
-					end )
-			else
-				fail ( mess:xml () ) -- XXX
-			end
-		end, fail )
-end
-
-function F.list_subscriptions ( conn, to, node, success, fail )
-	iq.send ( conn, to, 'get',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-				subscriptions = { node = node },
-			},
-		},
-		function ( mess )
-			local s = mess:path ( 'pubsub', 'subscriptions' )
-			if s then
-				local sub = s:child ()
-				local ret = { }
-				while sub do
-					table.insert ( ret, { jid = sub:attribute ( 'jid' ), subscription = sub:attribute ( 'subscription' ), subid = sub:attribute ( 'subid' ) } )
-					sub = sub:next ()
-				end
-				success ( ret )
-			else
-				fail ( mess:xml () ) -- XXX
-			end
-		end, fail )
-end
-
-function F.modify_subscription ( conn, to, node, jid, state, success, fail, id )
-	iq.send ( conn, to, 'set',
-		{
-			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
-				subscriptions = { node = node,
-					subscription = { jid = jid, subscription = state, id = id },
-				},
-			},
-		}, success, fail )
-end
-
-return F
+	end
 
 -- vim: se ts=4: --
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/register.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -0,0 +1,81 @@
+
+local lm          = require 'lm'
+local iq_register = require 'lm.iq_register'
+
+main.command ( 'register',
+	function ( args )
+		local who
+		if args and args ~= '' then
+			who = args
+		else
+			who = main.full_jid ()
+		end
+		iq_register.register ( lm.connection.bless ( main.connection () ), who,
+			function ( form, submit, reject )
+				insert_form ( form,
+					function ( form )
+						submit ( form,
+							function ()
+								main.print_info ( who, 'Successfully registered' )
+							end,
+							function ( mesg )
+								main.print_info ( who, 'Registration failed: ' .. mesg )
+							end )
+					end,
+					function ( form )
+						reject ( form,
+							function ()
+								main.print_info ( who, 'Registration cancelled' )
+							end,
+							function ( mesg )
+								main.print_info ( who, 'Registration cancellation failed: ' .. mesg )
+							end )
+					end )
+			end,
+			function ( mesg )
+				main.print_info ( who, 'Registration failed: ' .. mesg )
+			end )
+	end, false, 'jid' )
+main.command ( 'cancel',
+	function ( args )
+		local who
+		if args and args ~= '' then
+			who = args
+		else
+			who = main.full_jid ()
+		end
+		iq_register.unregister ( lm.connection.bless ( main.connection () ), who,
+			function ( form, submit, reject )
+				if not form then
+					main.print_info ( who, 'Successfully unregistered' )
+				else
+					insert_form ( form,
+						function ( form )
+							submit ( form,
+								function ()
+									main.print_info ( who, 'Successfully unregistered' )
+								end,
+								function ( mesg )
+									main.print_info ( who, 'Unregistrering failed: ' .. mesg )
+								end )
+						end,
+						function ( form )
+							reject ( form,
+								function ()
+									main.print_info ( who, 'Unregistration cancelled' )
+								end,
+								function ( mesg )
+									main.print_info ( who, 'Unregistration cancellation failed: ' .. mesg )
+								end )
+						end )
+				end
+			end,
+			function ( mesg )
+				main.print_info ( who, 'Unregistering failed: ' .. mesg )
+			end )
+	end, false, 'jid' )
+
+commands_help['register'] = "[jid]\n\nSends registration request to jid (or current buddy). You, probably, then will need to fill and send some form."
+commands_help['cancel'] = "[jid]\n\nSends registration cancellation request to jid (or current buddy). May require a form filling."
+
+-- vim: se ts=4: --
--- a/examples/remote.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/remote.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,64 +1,66 @@
 
--- REMOTE CONTROLLING CLIENTS (XEP-0146)
-
--- library
-
-local iq     = require 'iq'
-local x_data = require 'x_data'
-local disco  = require 'disco'
-
--- public
-
-local F = { }
-
-function F.list ( conn, to, success, fail )
-	disco.items ( conn, to, success, fail, 'http://jabber.org/protocol/commands' )
-end
+local lm     = require 'lm'
+local remote = require 'lm.remote'
 
-function F.command ( conn, to, command, success, fail )
-	iq.send ( conn, to, 'set',
-		{
-			command = { xmlns = 'http://jabber.org/protocol/commands', action = 'execute', node = command },
-		},
-		function ( mess )
-			local c = mess:child ( 'command' )
-			if c then
-				local status = c:attribute ( 'status' )
-				if status == 'completed' then
-					success ()
-				else
-					local x = c:child ( 'x' )
-					if x then
-						local sid = c:attribute ( 'sessionid' )
-						success ( x_data.parse ( x ),
-							function ( form, success, fail )
-								iq.send ( conn, to, 'set',
-									{
-										command = form:format ( { xmlns = 'http://jabber.org/protocol/commands', node = command, sessionid = sid }, 'submit' ),
-									},
-									function ( mess )
-										local c = mess:child ( 'command' )
-										if c and c:attribute ( 'status' ) == 'completed' then
-											success ()
-										else
-											fail ( mess:xml () ) -- XXX more forms? results?
-										end
-									end, fail )
+main.command ( 'remote',
+	function ( args )
+		local who
+		if args.t then
+			who = args.t
+		else
+			who = main.full_jid ()
+		end
+		local action = args[1]
+		local conn   = lm.connection.bless ( main.connection () )
+		if action then
+			remote.command ( conn, who, action,
+				function ( form, submit, reject )
+					if not form then
+						main.print_info ( who, ('Command %s completed'):format ( action ) )
+					else
+						insert_form ( form, -- XXX
+							function ( form )
+								submit ( form,
+									function ()
+										main.print_info ( who, ('Command %s completed'):format ( action ) )
+									end,
+									function ( mesg )
+										main.print_info ( who, ('Command %s execution failed: %s'):format ( action, mesg ) )
+									end )
 							end,
-							function ( form, success, fail )
-								iq.send ( conn, to, 'set',
-									{
-										command = form:format ( { xmlns = 'http://jabber.org/protocol/commands', node = command, sessionid = sid }, 'cancel' ),
-									}, success, fail )
+							function ( form )
+								reject ( form,
+									function ()
+										main.print_info ( who, ('Command %s execution cancelled'):format ( action ) )
+									end,
+									function ( mesg )
+										main.print_info ( who, ('Command %s execution cancellation failed: %s'):format ( action, mesg ) )
+									end )
 							end )
+					end
+				end,
+				function ( mesg )
+					main.print_info ( who, ('Command %s execution failed: %s'):format ( action, mesg ) )
+				end )
+		else
+			remote.list ( conn, who,
+				function ( items )
+					local text = ''
+					for index, item in ipairs ( items ) do
+						text = text .. '\n - ' .. item.node
+					end
+					if text ~= '' then
+						main.print_info ( who, 'Available commands:' .. text )
 					else
-						fail ( mess:xml () ) -- XXX
+						main.print_info ( who, 'No commands available.' )
 					end
-				end
-			end
-		end, fail )
-end
+				end,
+				function ( mesg )
+					main.print_info ( who, ("Remote commands list for %s failed: %s"):format ( who, mesg ) )
+				end )
+		end
+	end, true, 'jid' )
 
-return F
+commands_help['remote'] = "[-t target_jid] [remote_command]\n\nPrints list of available remote command or requests execution of specified command."
 
 -- vim: se ts=4: --
--- a/examples/tune.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/tune.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,24 +1,147 @@
-
--- USER TUNE (XEP-0118)
 
--- library
+local mpd    = require 'mpd'
+local lm     = require 'lm'
+local pubsub = require 'lm.pubsub'
 
-local pep = require 'pep'
+local tune_enabled = false
+local mpd_pub_song = { }
 
---
-
-local F = { }
+pubsub.handler ( 'http://jabber.org/protocol/tune',
+	function ( from, node, data )
+		local self = false
+		if from == lm.connection.bless ( main.connection () ):jid():gsub ( '/.*', '' ) then -- o_O
+			self         = true
+			mpd_pub_song = { }
+		end
+		local item = data:child ()
+		local text = ''
+		while item do
+			local name  = item:name ()
+			local value = item:value ()
+			if self then
+				mpd_pub_song[name] = value or ''
+			end
+			text = ("%s\n- %s: %s"):format ( text, item:name (), item:value () or '' )
+			item = item:next ()
+		end
+		if main.yesno ( main.option ( 'lua_pep_notification' ) ) then
+			if text ~= '' then
+				text = 'Now listening to:' .. text
+			else
+				text = 'Now not listening to anything'
+			end
+		end
+		main.print_info ( from, text )
+		return true
+	end )
 
-function F.publish ( conn, node, success, fail, data )
-	local item = { xmlns = 'http://jabber.org/protocol/tune' }
-	if data then
-		for key, value in pairs ( data ) do
-			item[key] = { value }
+local function mpd_getstatus ()
+	local status = mpd.call_command { 'status' }
+	if not tune_enabled or ( status.state ~= 'play' and status.state ~= 'pause' ) then
+		for k, v in pairs ( mpd_pub_song ) do -- if there is anything published, publish nothing
+			return { }
+		end
+		return nil
+	end
+	
+	local song      = mpd.call_command { 'currentsong' }
+	local dir, file = song.file:match ( '(.+)/(.-)' )
+	-- populate according to currentsong fields: artist - artist, length - time, source - album, title - title, track - id, rating - ?, uri - ?
+	local ret = {
+		artist = song.artist or 'Unknown',
+		length = song.time,
+		source = song.album  or dir,
+		title  = song.title  or file,
+		track  = song.id,
+	}
+
+	if not song.time or song.time == '0' then -- XXX
+		ret.length = nil
+	end
+
+	local modified = false
+	for k, v in pairs ( ret ) do
+		if mpd_pub_song[k] ~= v then
+			modified = true
+			break
 		end
 	end
-	pep.publish ( conn, 'http://jabber.org/protocol/tune', { tune = item }, success, fail )
+	if not modified then
+		for k, v in pairs ( mpd_pub_song ) do
+			if ret[k] ~= v then
+				modified = true
+				break
+			end
+		end
+	end
+
+	if modified then
+		return ret
+	else
+		return nil
+	end
+end
+
+local function mpd_callback ()
+	local sdata = mpd_getstatus ()
+	if sdata then
+		tune.publish ( lm.connection.bless ( main.connection () ),
+			function ()
+			end,
+			function ( mesg )
+				print ( 'Error publishing tune: ' .. mesg )
+			end, sdata )
+	end
+	if tune_enabled then
+		return true
+	else
+		return false
+	end
 end
 
-return F
+-- do not call it too fast, or you end up with many daemons at once
+local function enable_tune ( yn )
+	if yn == nil then
+		yn = true
+	end
+	if yn then
+		if not tune_enabled then
+			main.timer ( 15, mpd_callback )
+			tune_enabled = true
+			-- update status
+		end
+	else
+		if tune_enabled then
+			tune_enabled = false
+			-- update status
+		end
+	end
+end
+
+main.command ( 'tune',
+	function ( args )
+		local enable = main.yesno ( args )
+		if enable == nil then
+			if tune_enabled then
+				print ( "Tune notifications enabled" )
+			else
+				print ( "Tune notifications disabled" )
+			end
+		else
+			enable_tune ( enable )
+		end
+	end, false, 'yesno' )
+
+commands_help['tune'] = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables publishing of notifications about playing music in your player (currently only mpd is supported)."
+
+hooks_d['hook-post-connect'].tune =
+	function ( args )
+		if tune_enabled then
+			mpd_callback ()
+		end
+	end
+
+main.add_feature ( 'http://jabber.org/protocol/tune+notify' )
+main.add_feature ( 'http://jabber.org/protocol/tune' )
 
 -- vim: se ts=4: --
--- a/examples/vcard.lua	Sat Mar 28 19:43:12 2009 +0200
+++ b/examples/vcard.lua	Tue Mar 31 18:35:34 2009 +0300
@@ -1,108 +1,34 @@
 
--- VCARD-TEMP (XEP-0054)
-
--- library
-
-local iq         = require 'iq'
-local form_field = require 'form_field'
-
---
-
-local F = { }
-local M = { }
-M.__index = M
-
-function F.new ( args )
-	local form = {
-		xmlns        = 'vcard-temp',
-		ftype        = args.type or 'form',
-		title        = args.title,
-		instructions = args.instructions,
-		f            = { },
-	}
-	setmetatable ( form, M )
-	return form
-end
-
-local function vcard_parse ( node, path, form )
-	local item = node:child ()
-	if item then
-		while item do
-			vcard_parse ( item, ( path and path .. '/' or '' ) .. item:name (), form )
-			item = item:next ()
-		end
-	elseif path then
-		form:add ( path, { type = 'text-single', value = node:value () } )
-	end
-end
-
-function F.parse ( card )
-	local form = F.new { type = 'form' }
-	vcard_parse ( card, nil, form )
-	return form
-end
-
-function M.type ( form )
-	return form.ftype
-end
-
-function M.desc ( form )
-	return form.title, form.instructions
-end
+local lm    = require 'lm'
+local vcard = require 'lm.vcard'
 
-function M.format ( form, root, format_as )
-	root.vCard = { xmlns = 'vcard-temp' }
-	for k, field in form:fields () do
-		local el = root.vCard
-		for k in field:name():gmatch ( '[^/]+' ) do
-			if not el[k] then
-				el[k] = { }
-			end
-			el = el[k]
-		end
-		el[1] = field:value ()
-	end
-	return root
-end
-
-function M.fields ( form )
-	return ipairs ( form.f )
-end
-
-function M.add ( form, name, fld )
-	fld.var   = name
-	fld.index = #form.f + 1
-	local obj = form_field.new ( fld )
-	table.insert ( form.f, obj )
-	form.f[name] = obj
-	return obj
-end
-
-function M.field ( form, name )
-	return form.f[name]
-end
-
-function F.retrieve ( conn, from, success, fail )
-	iq.send ( conn, from, 'get',
-		{
-			vCard = { xmlns = 'vcard-temp' },
-		},
-		function ( mess )
-			local card = mess:child ( 'vCard' )
-			if card and card:attribute ( 'xmlns' ) == 'vcard-temp' then
-				success ( F.parse ( mess:child ( 'vCard' ) ),
-					function ( form, success, fail )
-						iq.send ( conn, from, 'set', form:format ( form, { }, 'submit' ), success, fail )
+main.command ( 'vcard-temp',
+	function ( args )
+		vcard.retrieve ( lm.connection.bless ( main.connection () ), args[1],
+			function ( form, submit, reject )
+				insert_form ( form,
+					function ( form )
+						submit ( form,
+							function ()
+								print ( 'Vcard changed' )
+							end,
+							function ( mesg )
+								print ( 'Vcard changing error: ' .. mesg )
+							end )
 					end,
-					function ( form, success, fail )
-						success ()
+					function ( form )
+						reject ( form,
+							function ()
+								print ( 'Vcard changing cancelled' )
+							end,
+							function ( mesg )
+								print ( 'Vcard changing cancellation error: ' .. mesg )
+							end )
 					end )
-			else
-				fail ( mess:xml () ) -- XXX
-			end
-		end, fail )
-end
-
-return F
+			end,
+			function ( mesg )
+				print ( 'Vcard obtaining error: ' .. mesg )
+			end )
+	end, true, 'jid' )
 
 -- vim: se ts=4: --
--- a/examples/x_data.lua	Sat Mar 28 19:43:12 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-
--- DATA FORMS (XEP-0004)
-
--- library
-
-local form_field = require 'form_field'
-
---
-
-local F = { }
-local M = { }
-M.__index = M
-
--- form.new       -- new form
--- form.parse     -- parse existing form -- according to form type!
--- form:type      -- get type
--- form:desc      -- get title, instructions
--- form:format    -- format form -- according to form type!
--- form:fields    -- iterator, returns field objects
--- form:add       -- create field
--- form:field     -- get field
--- ?? form:del    -- delete field
--- field.new      -- create field
--- field:index    -- get index
--- field:type     -- get type
--- field:name     -- get var
--- field:desc     -- get label, desc
--- field:value    -- get/set value
--- field:values   -- get values
--- field:clear    -- clear value
--- field:options  -- get options
--- field:required -- get required
-
--- form    may have 'fixed' fields and should be ordered
--- submit  must not have 'fixed' fields and may be not ordered
--- cancel  must not have anything
--- result  should not have 'fixed', may be not ordered and can contain reported/item... multiple instances.
-
-function F.new ( args )
-	local form  = {
-		xmlns        = 'jabber:x:data',
-		ftype        = args.type or 'form',
-		title        = args.title,
-		instructions = args.instructions,
-		f            = { },
-	}
-	setmetatable ( form, M )
-	return form
-end
-
-function F.parse ( x )
-	local title = x:child ( 'title' )
-	if title then
-		title = title:value ()
-	end
-
-	local instructions = x:child ( 'instructions' )
-	if instructions then
-		instructions = instructions:value ()
-	end
-
-	local f = F.new { type = x:attribute ( 'type' ), title = title, instructions = instructions }
-
-	local field = x:child ()
-	while field do
-		local name = field:name ()
-		if name == 'field' then
-			local ftype = field:attribute ( 'type' ) or 'text-single'
-			local var   = field:attribute ( 'var' )
-			local r = {
-				type  = ftype,
-				label = field:attribute ( 'label' ),
-			}
-
-			local desc = field:child ( 'desc' )
-			if desc then
-				r.desc = desc:value ()
-			end
-
-			if field:child ( 'required' ) then
-				r.required = true
-			end
-
-			if ftype == 'jid-multi' or ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'list-single' then
-				local values  = { }
-				local options = { }
-
-				local item = field:child ()
-				while item do
-					local name = item:name ()
-					if name == 'value' then
-						table.insert ( values, item:value () )
-					elseif name == 'option' then
-						options[item:child( 'value' ):value ()] = item:attribute ( 'label' ) or ''
-					end
-					item = item:next ()
-				end
-
-				if ftype == 'list-single' then
-					local value = field:child ( 'value' )
-					if value then
-						r.value = value:value ()
-					end
-				else
-					r.value = values
-				end
-
-				if ftype == 'list-multi' or ftype == 'list-single' then
-					r.options = options
-				end
-			else
-				local value = field:child ( 'value' )
-				if value then
-					r.value = value:value ()
-				end
-			end
-
-			f:add ( var, r )
-		end
-
-		field = field:next ()
-	end
-
-	return f
-end
-
-function M.type ( form )
-	return form.ftype
-end
-
-function M.desc ( form )
-	return form.title, form.instructions
-end
-
-function M.format ( form, root, format_as )
-	local ft = format_as or form:type ()
-
-	root.x = { xmlns = 'jabber:x:data', type = ft }
-
-	if ft == 'cancel' then
-		return root
-	end
-
-	if ft == 'form' then
-		local title, instructions = form:desc ()
-		if title then
-			root.x.title = { title }
-		end
-		if instructions then
-			root.x.instructions = { instructions }
-		end
-	end
-
-	local fields = { }
-	for i, field in form:fields () do
-		local ftype = field:type ()
-
-		local options, label, desc, required
-		if ft == 'form' then
-			label, desc = field:desc ()
-			if desc then
-				desc = { desc }
-			end
-
-			if field:required () then
-				required = { }
-			end
-		
-			for option, label in field:options () do
-				table.insert ( options, { label = label, value = { option } } )
-			end
-		end
-
-		local value
-		if ftype == 'list-multi' or ftype == 'text-multi' or ftype == 'jid-multi' then
-			if ft == 'form' then
-				for option, label in field:options () do
-					table.insert ( options, { label = label, value = { option } } )
-				end
-			end
-
-			value = { }
-			for j, v in ipairs ( field.value ) do
-				table.insert ( value, { v } )
-			end
-		else
-			value = { field:value () }
-		end
-
-		if ftype ~= 'fixed' or ft == 'form' then
-			table.insert ( fields, {
-					type   = ftype,
-					var    = field:name (),
-					label  = label,
-					desc   = desc,
-					value  = value,
-					option = options
-				} )
-		end
-	end
-	root.x.field = fields
-
-	return root
-end
-
-function M.fields ( form )
-	return ipairs ( form.f )
-end
-
-function M.add ( form, var, fld )
-	fld.var   = var
-	fld.index = #form.f + 1 -- XXX
-	local obj = form_field.new ( fld )
-	table.insert ( form.f, obj )
-	form.f[var] = obj
-	return obj
-end
-
-function M.field ( form, var )
-	-- works well on indices
-	return form.f[var]
-end
-
-return F
-
--- vim: se ts=4: --