examples/pubsub.lua
author Myhailo Danylenko <isbear@ukrpost.net>
Sat, 21 Mar 2009 15:43:35 +0200
changeset 47 aba4bbe32cba
child 51 a95a3a73482c
permissions -rw-r--r--
Pubsub use lib interface


-- PUBLISH-SUBSCRIBE (BEP-0060)

-- library

require 'lm'
require 'iq'

-- public

pubsub = { }

-- SUBSCRIBER USE CASES

function pubsub.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 pubsub.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 pubsub.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:children ()
				while item do
					success ( from, node, item ) -- XXX use registered xmlns handlers for that?
					item = item:next ()
				end
			end
		end, fail )
end

-- OWNER USE CASES

-- node may be nil
function pubsub.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 pubsub.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 pubsub.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 pubsub.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 ) -- FIXME
			local x = mess:path ( 'pubsub', 'configure', 'x' )
			if x then
				local fid = parse_form ( x )
				forms[fid].send =
					function ( form )
						iq.send ( conn, to, 'set',
							{
								pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
									configure = { node = node,
										x = { xmlns = 'jabber:x:data', type = 'submit',
											field = form.val,
										},
									},
								},
							},
							function ( mess )
								success ()
								main.print_info ( to, 'Now you can run /form del ' .. fid .. ' to delete form from list' )
								form.status = 'acquired'
							end,
							function ( mesg )
								if mesg then
									form.status = 'rejected'
								else
									form.status = 'unknown'
								end
								fail ( mesg )
							end )
						form.status = 'sent'
					end
				forms[fid].status = 'filling'
				main.print_info ( to, 'You have new form. To fill it, use /form ' .. fid .. ' fieldname value' )
			else
				main.print_info ( to, 'Weird, no error and no node configuration form: ' .. mess:xml () )
			end
		end, fail )
end

function pubsub.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:children ()
				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 pubsub.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

-- mcabber

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 ()
					main.print_info ( who, 'Node configuration accepted' )
				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."
--]]

-- vim: se ts=4: --