examples/pubsub.lua
changeset 66 542f61e113cb
parent 64 bf7521ed96eb
child 67 d33ca5572e91
equal deleted inserted replaced
65:f1be8dbb209c 66:542f61e113cb
     1 
     1 
     2 -- PUBLISH-SUBSCRIBE (BEP-0060)
     2 -- PUBLISH-SUBSCRIBE (BEP-0060)
     3 
     3 
     4 -- library
     4 -- library
     5 
     5 
     6 require 'lm'
     6 local lm = require 'lm'
     7 local iq = require 'iq'
     7 local iq = require 'iq'
     8 require 'x_data'
     8 require 'x_data'
     9 
     9 
    10 -- public
    10 --
    11 
    11 
    12 pubsub = { }
    12 local O = {
       
    13 	handlers = { },
       
    14 }
       
    15 
       
    16 local F = { }
       
    17 
       
    18 function F.handler ( xmlns, handler )
       
    19 	O.handlers[xmlns] = handler
       
    20 end
       
    21 
       
    22 function F.message_handler ( conn, mess )
       
    23 	local e = mess:child ( 'event' )
       
    24 	if e and e:attribute ( 'xmlns' ) == 'http://jabber.org/protocol/pubsub#event' then
       
    25 		local is = e:child ( 'items' )
       
    26 		if is then
       
    27 			local from = mess:attribute ( 'from' )
       
    28 			local node = is:attribute ( 'node' )
       
    29 			local item = is:child ()
       
    30 			while item do
       
    31 				local id = item:attribute ( 'id' )
       
    32 				local n  = item:child ()
       
    33 				while n do
       
    34 					local xmlns = n:attribute ( 'xmlns' )
       
    35 					if O.handlers[xmlns] then
       
    36 						O.handlers[xmlns] ( from, node, n, id )
       
    37 					end
       
    38 					n = n:next ()
       
    39 				end
       
    40 				item = item:next ()
       
    41 			end
       
    42 			return true
       
    43 		end
       
    44 	end
       
    45 	return false
       
    46 end
    13 
    47 
    14 -- SUBSCRIBER USE CASES
    48 -- SUBSCRIBER USE CASES
    15 
    49 
    16 function pubsub.subscribe ( conn, to, node, success, fail )
    50 function F.subscribe ( conn, to, node, success, fail )
    17 	local jid = conn:jid():gsub ( '/.*', '' )
    51 	local jid = conn:jid():gsub ( '/.*', '' )
    18 	iq.send ( conn, to, 'set',
    52 	iq.send ( conn, to, 'set',
    19 		{
    53 		{
    20 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
    54 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
    21 				subscribe = { node = node, jid = jid },
    55 				subscribe = { node = node, jid = jid },
    30 			end
    64 			end
    31 		end,
    65 		end,
    32 		fail )
    66 		fail )
    33 end
    67 end
    34 
    68 
    35 function pubsub.unsubscribe ( conn, to, node, success, fail )
    69 function F.unsubscribe ( conn, to, node, success, fail )
    36 	local jid = conn:jid():gsub ( '/.*', '' )
    70 	local jid = conn:jid():gsub ( '/.*', '' )
    37 	iq.send ( conn, to, 'set',
    71 	iq.send ( conn, to, 'set',
    38 		{
    72 		{
    39 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
    73 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
    40 				unsubscribe = { node = node, jid = jid },
    74 				unsubscribe = { node = node, jid = jid },
    43 end
    77 end
    44 
    78 
    45 -- I found no servers with subscription options support thus it is not implemented.
    79 -- I found no servers with subscription options support thus it is not implemented.
    46 
    80 
    47 -- untested :(
    81 -- untested :(
    48 function pubsub.retrieve ( conn, to, node, success, fail, max, ids )
    82 function F.retrieve ( conn, to, node, success, fail, max, ids )
    49 	local items = { node = node, max_items = max }
    83 	local items = { node = node, max_items = max }
    50 	if ids then
    84 	if ids then
    51 		items.item = { }
    85 		items.item = { }
    52 		for k, id in pairs ( ids ) do
    86 		for k, id in pairs ( ids ) do
    53 			table.insert ( items.item, { id = id } )
    87 			table.insert ( items.item, { id = id } )
    62 		function ( mess )
    96 		function ( mess )
    63 			local items = mess:path ( 'pubsub', 'items' )
    97 			local items = mess:path ( 'pubsub', 'items' )
    64 			if items then
    98 			if items then
    65 				local from = mess:attribute ( 'from' )
    99 				local from = mess:attribute ( 'from' )
    66 				local node = items:attribute ( 'node' )
   100 				local node = items:attribute ( 'node' )
    67 				local item = items:children ()
   101 				local item = items:child ()
    68 				while item do
   102 				while item do
    69 					success ( from, node, item ) -- XXX use registered xmlns handlers for that?
   103 					success ( from, node, item ) -- XXX use registered xmlns handlers for that?
    70 					item = item:next ()
   104 					item = item:next ()
    71 				end
   105 				end
    72 			else
   106 			else
    76 end
   110 end
    77 
   111 
    78 -- OWNER USE CASES
   112 -- OWNER USE CASES
    79 
   113 
    80 -- node may be nil
   114 -- node may be nil
    81 function pubsub.create_node ( conn, to, node, success, fail )
   115 function F.create_node ( conn, to, node, success, fail )
    82 	iq.send ( conn, to, 'set',
   116 	iq.send ( conn, to, 'set',
    83 		{
   117 		{
    84 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
   118 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub',
    85 				create = { node = node },
   119 				create = { node = node },
    86 			},
   120 			},
    97 				end
   131 				end
    98 			end
   132 			end
    99 		end, fail )
   133 		end, fail )
   100 end
   134 end
   101 
   135 
   102 function pubsub.delete_node ( conn, to, node, success, fail )
   136 function F.delete_node ( conn, to, node, success, fail )
   103 	iq.send ( conn, to, 'set',
   137 	iq.send ( conn, to, 'set',
   104 		{
   138 		{
   105 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   139 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   106 				delete = { node = node },
   140 				delete = { node = node },
   107 			},
   141 			},
   108 		}, success, fail )
   142 		}, success, fail )
   109 end
   143 end
   110 
   144 
   111 function pubsub.purge_node ( conn, to, node, success, fail )
   145 function F.purge_node ( conn, to, node, success, fail )
   112 	iq.send ( conn, to, 'set',
   146 	iq.send ( conn, to, 'set',
   113 		{
   147 		{
   114 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   148 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   115 				purge = { node = node },
   149 				purge = { node = node },
   116 			},
   150 			},
   117 		}, success, fail )
   151 		}, success, fail )
   118 end
   152 end
   119 
   153 
   120 function pubsub.configure_node ( conn, to, node, success, fail )
   154 function F.configure_node ( conn, to, node, success, fail )
   121 	iq.send ( conn, to, 'get',
   155 	iq.send ( conn, to, 'get',
   122 		{
   156 		{
   123 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   157 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   124 				configure = { node = node },
   158 				configure = { node = node },
   125 			},
   159 			},
   151 				fail ( mess:xml () ) -- XXX
   185 				fail ( mess:xml () ) -- XXX
   152 			end
   186 			end
   153 		end, fail )
   187 		end, fail )
   154 end
   188 end
   155 
   189 
   156 function pubsub.list_subscriptions ( conn, to, node, success, fail )
   190 function F.list_subscriptions ( conn, to, node, success, fail )
   157 	iq.send ( conn, to, 'get',
   191 	iq.send ( conn, to, 'get',
   158 		{
   192 		{
   159 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   193 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   160 				subscriptions = { node = node },
   194 				subscriptions = { node = node },
   161 			},
   195 			},
   162 		},
   196 		},
   163 		function ( mess )
   197 		function ( mess )
   164 			local s = mess:path ( 'pubsub', 'subscriptions' )
   198 			local s = mess:path ( 'pubsub', 'subscriptions' )
   165 			if s then
   199 			if s then
   166 				local sub = s:children ()
   200 				local sub = s:child ()
   167 				local ret = { }
   201 				local ret = { }
   168 				while sub do
   202 				while sub do
   169 					table.insert ( ret, { jid = sub:attribute ( 'jid' ), subscription = sub:attribute ( 'subscription' ), subid = sub:attribute ( 'subid' ) } )
   203 					table.insert ( ret, { jid = sub:attribute ( 'jid' ), subscription = sub:attribute ( 'subscription' ), subid = sub:attribute ( 'subid' ) } )
   170 					sub = sub:next ()
   204 					sub = sub:next ()
   171 				end
   205 				end
   174 				fail ( mess:xml () ) -- XXX
   208 				fail ( mess:xml () ) -- XXX
   175 			end
   209 			end
   176 		end, fail )
   210 		end, fail )
   177 end
   211 end
   178 
   212 
   179 function pubsub.modify_subscription ( conn, to, node, jid, state, success, fail, id )
   213 function F.modify_subscription ( conn, to, node, jid, state, success, fail, id )
   180 	iq.send ( conn, to, 'set',
   214 	iq.send ( conn, to, 'set',
   181 		{
   215 		{
   182 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   216 			pubsub = { xmlns = 'http://jabber.org/protocol/pubsub#owner',
   183 				subscriptions = { node = node,
   217 				subscriptions = { node = node,
   184 					subscription = { jid = jid, subscription = state, id = id },
   218 					subscription = { jid = jid, subscription = state, id = id },
   185 				},
   219 				},
   186 			},
   220 			},
   187 		}, success, fail )
   221 		}, success, fail )
   188 end
   222 end
   189 
   223 
   190 -- mcabber
   224 return F
   191 
       
   192 main.command ( 'node',
       
   193 	function ( args )
       
   194 		local who, action, node = args.t, args[1], args[2]
       
   195 		local conn = lm.connection.bless ( main.connection () )
       
   196 		if not who then
       
   197 			who = main.current_buddy ()
       
   198 		end
       
   199 		if action == 'subscribe' then
       
   200 			pubsub.subscribe ( conn, who, node,
       
   201 				function ( id )
       
   202 					if id then
       
   203 						main.print_info ( who, 'Subscription succeeds with id ' .. id )
       
   204 					else
       
   205 						main.print_info ( who, 'Subscription successful' )
       
   206 					end
       
   207 				end,
       
   208 				function ( mesg )
       
   209 					main.print_info ( who, 'Subscription unsuccessful: ' .. mesg )
       
   210 				end )
       
   211 		elseif action == 'unsubscribe' then
       
   212 			pubsub.unsubscribe ( conn, who, node,
       
   213 				function ()
       
   214 					main.print_info ( who, 'Unubscription successful' )
       
   215 				end,
       
   216 				function ( mesg )
       
   217 					main.print_info ( who, 'Unsubscription unsuccessful: ' .. mesg )
       
   218 				end )
       
   219 		elseif action == 'retrieve' or action == 'items' or action == 'get' then
       
   220 			pubsub.retrieve ( conn, who, node,
       
   221 				function ( from, node, item )
       
   222 					main.print_info ( who, 'Item from ' .. from .. ', node ' .. node .. ':\n' .. item:xml () ) 
       
   223 				end,
       
   224 				function ( mesg )
       
   225 					main.print_info ( who, 'Retrieval failed: ' .. mesg )
       
   226 				end, args.m )
       
   227 		elseif action == 'create' or action == 'new' then
       
   228 			pubsub.create_node ( conn, who, node,
       
   229 				function ( node )
       
   230 					if node then
       
   231 						main.print_info ( who, 'Node ' .. node .. ' successfully created' )
       
   232 					else
       
   233 						main.print_info ( who, 'Node successfully created' )
       
   234 					end
       
   235 				end,
       
   236 				function ( mesg )
       
   237 					main.print_info ( who, 'Creation failed: ' .. mesg )
       
   238 				end )
       
   239 		elseif action == 'delete' or action == 'del' then
       
   240 			pubsub.delete_node ( conn, who, node,
       
   241 				function ()
       
   242 					main.print_info ( who, 'Node deleted' )
       
   243 				end,
       
   244 				function ( mesg )
       
   245 					main.print_info ( who, 'Node deletion failed: ' .. mesg )
       
   246 				end )
       
   247 		elseif action == 'purge' or action == 'del_items' then
       
   248 			pubsub.purge_node ( conn, who, node,
       
   249 				function ()
       
   250 					main.print_info ( who, 'Node purged' )
       
   251 				end,
       
   252 				function ( mesg )
       
   253 					main.print_info ( who, 'Node purge failed: ' .. mesg )
       
   254 				end )
       
   255 		elseif action:sub ( 1, 4 ) == 'conf' then
       
   256 			pubsub.configure_node ( conn, who, node,
       
   257 				function ( form, submit, reject )
       
   258 					local id = #forms + 1
       
   259 					forms[id] = {
       
   260 						form = form,
       
   261 						submit =
       
   262 							function ( form )
       
   263 								submit ( form,
       
   264 									function ()
       
   265 										main.print_info ( who, 'Node configuration completed' )
       
   266 									end,
       
   267 									function ( mesg )
       
   268 										main.print_info ( who, 'Node configuration failed: ' .. mesg )
       
   269 									end )
       
   270 							end,
       
   271 						reject =
       
   272 							function ( form )
       
   273 								reject ( form,
       
   274 									function ()
       
   275 										main.print_info ( who, 'Node configuration cancelled' )
       
   276 									end,
       
   277 									function ( mesg )
       
   278 										main.print_info ( who, 'Node configuration cancellation failed: ' .. mesg )
       
   279 									end )
       
   280 							end,
       
   281 					}
       
   282 					print ( 'You have new form ' .. id )
       
   283 				end,
       
   284 				function ( mesg )
       
   285 					main.print_info ( who, 'Node configuration failed: ' .. mesg )
       
   286 				end )
       
   287 		elseif action == 'subscriptions' or action == 'subscribers' then
       
   288 			pubsub.list_subscriptions ( conn, who, node,
       
   289 				function ( s )
       
   290 					local text = ''
       
   291 					for i, v in ipairs ( s ) do
       
   292 						local subid = v.subid
       
   293 						if subid then
       
   294 							subid = '(id ' .. subid .. ')'
       
   295 						else
       
   296 							subid = ''
       
   297 						end
       
   298 						text = text .. ('\n- [%s] %s %s'):format ( v.subscription, v.jid, subid )
       
   299 					end
       
   300 					if text ~= '' then
       
   301 						main.print_info ( who, 'Node subscriptions:' .. text )
       
   302 					else
       
   303 						main.print_info ( who, 'No subscriptions' )
       
   304 					end
       
   305 				end,
       
   306 				function ( mesg )
       
   307 					main.print_info ( who, 'Node subscriptions retrieval failed: ' .. mesg )
       
   308 				end )
       
   309 		elseif action == 'subscription' or action == 'modify' then -- XXX
       
   310 			pubsub.modify_subscription ( conn, args.t or main.current_buddy (), node, args[3], args[4],
       
   311 				function ()
       
   312 					main.print_info ( who, 'Subscription modified' )
       
   313 				end,
       
   314 				function ( mesg )
       
   315 					main.print_info ( who, 'Subsrciption modification failed: ' .. mesg )
       
   316 				end, args[5] )
       
   317 		else
       
   318 			print ( 'Error: unknown action' )
       
   319 		end
       
   320 	end, true )
       
   321 
       
   322 -- FIXME
       
   323 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?)"
       
   324 --[[
       
   325 commands_help['subscribe']      = "[-t jid] node_name\n\nSends pubsub subscription request to specified node of jid or current buddy."
       
   326 commands_help['unsubscribe']    = "[-t jid] node_name\n\nSends pubsub unsubscription request to specified node of jid or current buddy."
       
   327 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."
       
   328 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."
       
   329 commands_help['configure_node'] = "[-t jid] node_name\n\nSends pubsub node configuration request to specified node of jid or current buddy."
       
   330 commands_help['delete_node']    = "[-t jid] node_name\n\nSends pubsub node deletion request to specified node of jid or current buddy."
       
   331 commands_help['purge_node']     = "[-t jid] node_name\n\nSends pubsub node items purging request to specified node of jid or current buddy."
       
   332 commands_help['subscriptions']  = "[-t jid] node_name\n\nSends pubsub subscription list request to specified node of jid or current buddy."
       
   333 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."
       
   334 --]]
       
   335 
   225 
   336 -- vim: se ts=4: --
   226 -- vim: se ts=4: --