Wed, 05 Jan 2011 05:14:02 +0000
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 };