util.xmllex, util.xmppstream: It runs

Wed, 05 Jan 2011 05:14:02 +0000

author
daurnimator <quae@daurnimator.com>
date
Wed, 05 Jan 2011 05:14:02 +0000
changeset 4002
2b53b4b5d46e
parent 4001
ed90aace9add
child 4003
cb6ddda1cb5f

util.xmllex, util.xmppstream: It runs

util/xmllex.lua file | annotate | diff | comparison | revisions
util/xmppstream.lua file | annotate | diff | comparison | revisions
--- a/util/xmllex.lua	Wed Jan 05 05:12:54 2011 +0000
+++ b/util/xmllex.lua	Wed Jan 05 05:14:02 2011 +0000
@@ -1,9 +1,11 @@
-local assert, ipairs , pairs , setmetatable , rawget , rawset , tostring =
-	assert, ipairs , pairs , setmetatable , rawget , rawset , tostring
-local strsub = string.sub
+local assert , ipairs , pairs , setmetatable , rawget , rawset , tostring =
+	assert , ipairs , pairs , setmetatable , rawget , rawset , tostring
+local strsub , strmatch = string.sub , string.match
 local tblconcat = table.concat
 local tblinsert = table.insert
 
+local stanza_methods = require "util.stanza".stanza_mt;
+
 local function getstring ( msgs , startpos , finishpos )
 	if #msgs == 1 then --All originated in same string
 		return strsub ( msgs[1] , startpos , finishpos )
@@ -61,7 +63,7 @@
 end
 
 local function handleinside ( str, r , initial )
-	local c , d , selfclosing = str:find ( "(/?)>" , initial )
+	local c , d , selfclosing = str:find ( "([/?]?)>" , initial )
 	if not c then
 		r.state = "inside"
 		return false
@@ -70,7 +72,7 @@
 	local m = r[#r]
 	m.finish = d
 	m.finishs = c - 1
-	if selfclosing == "/" then
+	if selfclosing == "/" or selfclosing == "?" then
 		m.type = "selfclosing"
 		r.depth = r.depth - 1
 	end
@@ -117,57 +119,132 @@
 	return r
 end
 
-local function process_starttag ( starttag )
-	local str = tostring ( starttag )
+local function get_name ( str )
+	return strmatch ( str , "^<([^%s>/]+)" )
+end
+
+local function get_attr ( str  )
 	local attr = { }
-	
-	local elem = str:match ( "[^%s=></]+" )
-	for name , quote, attvalue in str:gmatch ( [=[([^%s=<]+)%s*=%s*(["'])([^"]*)%2]=] ) do
+	for name , quote, attvalue in str:gmatch ( [=[([^%s=/<]+)%s*=%s*(["'])([^'"]*)%2]=] ) do
 		attr [ name ] = attvalue
 	end
-	return elem , attr
+	return attr
+end
+
+function resolve_attr_namespaces ( attr )
+	local namespace = { }
+	local prefixattr = { }
+	for k , attr_value in pairs ( attr ) do
+		local attr_prefix , attr_name = k:match ( "^([^:\1]+):?([^\1]-)$" )
+		
+		if attr_prefix == nil then
+			error ( "already resolved" )
+		elseif attr_prefix == "xmlns" then
+			namespace [ attr_name ] = attr_value
+		elseif #attr_name ~= 0 and attr_prefix ~= "xml" then
+			local t = prefixattr [ attr_prefix ]
+			if not t then
+				t = { }
+				prefixattr [ attr_prefix ] = t
+			end
+			t [ attr_name ] = attr_value
+		end
+	end
+
+	for k , v in pairs ( prefixattr ) do 
+		for name , value in pairs ( v ) do
+			attr [ namespace [ k ] .. "\1" .. name ] = value
+		end
+	end
+	return namespace
 end
 
+local currentindex = 1
+
+local function resolve_namespace ( element )
+	local parent = element.parent
+
+	local prefix = get_name ( element.str ):match ( "^([^:]+):" )
+
+	local namespace = setmetatable ( resolve_attr_namespaces ( element.attr ) , { __index = parent.namespace } )
+	
+	local current_namespace = prefix and ( namespace [ prefix ] or error ("unbound prefix: "..prefix) )
+		or rawget(element.attr, "xmlns")
+		or parent.attr.xmlns
+		or parent.namespace [ currentindex ]
+	namespace [ currentindex ] = current_namespace
+	
+	return namespace
+end
+
+local dynamic_properties = {
+	name = function ( t )
+		return get_name ( t.str ):match("[^:]+$")
+	end;
+	attr = function ( t , k )
+		return setmetatable ( get_attr ( t.str ) , { __index = function ( attr_table , attr )
+				local _ = t.namespace -- DO NOT OPTIMISE AWAY WAQAS
+				setmetatable ( attr_table , { __index = { xmlns = t.namespace[currentindex] } } )
+				return attr_table [ attr ]
+			end } )
+	end;
+	str = function ( t , k )
+		return tostring ( t.opentag )
+	end;
+	namespace = resolve_namespace ;
+}
+
 local stanza_mt = {
 	__index = function ( t , k )
-		if k == "name" or k == "attr" then
-			local elem , attr = process_starttag ( t.opentag )
-			rawset ( t , "name" , elem )
-			rawset ( t , "attr" , attr )
-			return rawget ( t , k )
+		local f = dynamic_properties [ k ]
+		if f then
+			local v = f ( t )
+			
+			rawset ( t , k , v )
+			return v
 		else
-			print("METHOD",k)
-			return stanza_methods [ k ]
+			return stanza_methods[k]
 		end
 	end ;
+	
 	__tostring = function ( t )
+		if t.modified then
+			return stanza_methods.__tostring ( t )
+		end
 		local opentag = t.opentag
 		local endtag = assert ( rawget ( t , "endtag" ) or rawget ( t , "selfclosing" ) and t.opentag )
 		return getstring ( opentag.msgs , opentag.start , endtag.finish )
 	end ;
+	
+	__newindex = function ( t , k , v )
+		rawset ( t , "modified", true )
+		rawset ( t , k , v )
+	end ;
 }
 
-local function new_stanza ( )
-	return setmetatable ( { tags = { } } , stanza_mt )
+local function new_stanza ( parent )
+	return setmetatable ( { tags = { } , parent = parent } , stanza_mt )
 end
 
-local function tagindex_to_tree(indices)
+local function tagindex_to_tree(indices, start, finish,root)
 	if not start then
 		start = 1
 		finish = #indices
 	end
 	
-	local root = { tags = { } }
+	root = root or { attr = { } }
+	root.namespace = resolve_attr_namespaces ( root.attr )
+	root.root = true
+	root.tags = { }
 	local leaf = root
 	
 	for i = start , finish do
 		local v = indices [ i ]
 		
 		if v.type == "selfclosing" then
-			local newleaf = new_stanza ( )
+			local newleaf = new_stanza ( leaf )
 			newleaf.opentag = v
 			newleaf.selfclosing = true
-			newleaf.parent = leaf
 			
 			tblinsert ( leaf , newleaf )
 			tblinsert ( leaf.tags , newleaf )
@@ -177,9 +254,8 @@
 		elseif v.type == "text" then
 			tblinsert ( leaf, v )
 		else -- Open tag
-			local newleaf = new_stanza ( )
+			local newleaf = new_stanza ( leaf )
 			newleaf.opentag = v
-			newleaf.parent = leaf
 			
 			tblinsert ( leaf , newleaf )
 			tblinsert ( leaf.tags , newleaf )
@@ -195,5 +271,7 @@
 return {
 	index = index ;
 	tagindex_to_tree = tagindex_to_tree ;
-	process_starttag = process_starttag ;
+	get_name = get_name ;
+	get_attr = get_attr ;
+	resolve_attr_namespaces = resolve_attr_namespaces ;
 };
--- a/util/xmppstream.lua	Wed Jan 05 05:12:54 2011 +0000
+++ b/util/xmppstream.lua	Wed Jan 05 05:14:02 2011 +0000
@@ -10,8 +10,8 @@
 
 local lex = require "util.xmllex";
 local index = lex.index
-local tagindex_to_tree  = lex.tagindex_to_tree
-local process_starttag = lex.process_starttag
+local tagindex_to_tree  = lex.tagindex_to_tree;
+local get_name, get_attr = lex.get_name, lex.get_attr;
 
 local default_log = require "util.logger".init("xmppstream");
 
@@ -26,35 +26,49 @@
 		reset = function ( )
 			partial = nil
 			openindexes = { }
+			lastindex = 0
 		end ;
 		feed = function ( stream , data )
-			while true do
-				partial = index ( data , partial )
-				for i = lastindex + 1 , #partial - 1 do
-					local v = partial [ i ]
-					
-					if v.finish then
-						if v.type == "open" then
-							tblinsert ( openindexes , i )
-							if #openindexes == 1 then
-								local name , attr = process_starttag ( v )
+			--print("FFEED", data)
+			partial = index ( data , partial )
+			local tmp = lastindex
+			lastindex = #partial - 1
+			for i = tmp + 1 , #partial - 1 do
+				local v = partial [ i ]
+				
+				if v.finish then
+					if v.type == "open" or v.type == "selfclosing" then
+						tblinsert ( openindexes , i )
+						if #openindexes == 1 then
+							local vstr = tostring(v);
+							local name = get_name ( vstr )
+							if name == "stream:stream" then
+								local attr = get_attr ( vstr );
 								stream_callbacks.streamopened ( session , attr )
 							end
-						elseif v.type == "close" then
-							if #openindexes == 2 then -- If closing level 2...
-								local stanza = tagindex_to_tree ( partial , openindexes [ #openindexes ] , i )
-								stream_callbacks.handlestanza ( session , stanza )
-							elseif #openindexes == 1 then
+						end
+					end
+					if v.type == "close" or v.type == "selfclosing" then
+						if #openindexes == 2 then -- If closing level 2...
+							local attr = get_attr ( tostring(partial[openindexes[1]]) )
+							local stanza = tagindex_to_tree ( partial , openindexes [ #openindexes ] , i , {
+								attr = attr,
+							} )
+							if stanza[1].attr.xmlns == attr.xmlns then
+								stanza[1].attr.xmlns = false;
+							end
+							stream_callbacks.handlestanza ( session , stanza[1] )
+						elseif #openindexes == 1 then
+							local prefix, name = get_name ( tostring(v) )
+							if name == "stream" then
 								stream_callbacks.streamclosed ( session )
 							end
-							--print ( "Closed level " .. #openindexes )
-							openindexes [ #openindexes ] = nil
 						end
+						openindexes [ #openindexes ] = nil
 					end
 				end
-				lastindex = #partial - 1
-				break
 			end
+			return true
 		end ;
 		set_session = function ( newsession ) session = newsession end
 	};

mercurial