dtd_gen.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. -- dtd_gen.lua
  2. --
  3. -- a DTD generator for wireshark
  4. --
  5. -- (c) 2006 Luis E. Garcia Ontanon <luis@ontanon.org>
  6. --
  7. -- Wireshark - Network traffic analyzer
  8. -- By Gerald Combs <gerald@wireshark.org>
  9. -- Copyright 1998 Gerald Combs
  10. --
  11. -- SPDX-License-Identifier: GPL-2.0-or-later
  12. if gui_enabled() then
  13. local xml_fld = Field.new("xml")
  14. local function dtd_generator()
  15. local displayed = {} -- whether or not a dtd is already displayed
  16. local dtds = {} -- the dtds
  17. local changed = {} -- whether or not a dtd has been modified
  18. local dtd -- the dtd being dealt with
  19. local dtd_name -- its name
  20. -- we'll tap onto every frame that has xml
  21. local ws = {} -- the windows for each dtd
  22. local w = TextWindow.new("DTD Generator")
  23. local function help()
  24. local wh = TextWindow.new("DTD Generator Help")
  25. -- XXX write help
  26. wh:set('DTD Generator Help\n')
  27. end
  28. local function get_dtd_from_xml(text,d,parent)
  29. -- obtains dtd information from xml
  30. -- text: xml to be parsed
  31. -- d: the current dtd (if any)
  32. -- parent: parent entity (if any)
  33. -- cleanup the text from useless chars
  34. text = string.gsub(text ,"%s*<%s*","<");
  35. text = string.gsub(text ,"%s*>%s*",">");
  36. text = string.gsub(text ,"<%-%-(.-)%-%->"," ");
  37. text = string.gsub(text ,"%s+"," ");
  38. while true do
  39. -- find the first tag
  40. local open_tag = string.match(text,"%b<>")
  41. if open_tag == nil then
  42. -- no more tags, we're done
  43. return true
  44. end
  45. local name = string.match(open_tag,"[%w%d_-]+")
  46. local this_ent = nil
  47. if d == nil then
  48. -- there's no current dtd, this is entity is it
  49. d = dtds[name]
  50. if d == nil then
  51. d = {ents = {}, attrs = {}}
  52. dtds[name] = d
  53. end
  54. dtd = d
  55. dtd_name = name
  56. end
  57. this_ent = d[name]
  58. if this_ent == nil then
  59. -- no entity by this name in this dtd, create it
  60. this_ent = {ents = {}, attrs = {}}
  61. d.ents[name] = this_ent
  62. changed[dtd_name] = true
  63. end
  64. if parent ~= nil then
  65. -- add this entity to its parent
  66. parent.ents[name] = 1
  67. changed[dtd_name] = true
  68. end
  69. -- add the attrs to the entity
  70. for att in string.gmatch(open_tag, "([%w%d_-]+)%s*=") do
  71. if not this_ent.attrs[att] then
  72. changed[dtd_name] = true
  73. this_ent.attrs[att] = true
  74. end
  75. end
  76. if string.match(open_tag,"/>") then
  77. -- this tag is "self closed" just remove it and continue
  78. text = string.gsub(text,"%b<>","",1)
  79. else
  80. local close_tag_pat = "</%s*" .. name .. "%s*>"
  81. if not string.match(text,close_tag_pat) then return false end
  82. local span,left = string.match(text,"%b<>(.-)" .. close_tag_pat .. "(.*)")
  83. if span ~= nil then
  84. -- recurse to find child entities
  85. if not get_dtd_from_xml(span,d,this_ent) then
  86. return false
  87. end
  88. end
  89. -- continue with what's left
  90. text = left
  91. end
  92. end
  93. return true
  94. end
  95. local function entity_tostring(name,entity_data)
  96. -- name: the name of the entity
  97. -- entity_data: a table containg the entity data
  98. -- returns the dtd text for that entity
  99. local text = ''
  100. text = text .. '\t<!ELEMENT ' .. name .. ' (' --)
  101. for e,j in pairs(entity_data.ents) do
  102. text = text .. " " .. e .. ' |'
  103. end
  104. text = text .. " #PCDATA ) >\n"
  105. text = text .. "\t<!ATTLIST " .. name
  106. for a,j in pairs(entity_data.attrs) do
  107. text = text .. "\n\t\t" .. a .. ' CDTATA #IMPLIED'
  108. end
  109. text = text .. " >\n\n"
  110. text = string.gsub(text,"<!ATTLIST " .. name .. " >\n","")
  111. return text
  112. end
  113. local function dtd_tostring(name,doctype)
  114. local text = '<? wireshark:protocol proto_name="' .. name ..'" hierarchy="yes" ?>\n\n'
  115. local root = doctype.ents[name]
  116. doctype.ents[name] = nil
  117. text = text .. entity_tostring(name,root)
  118. for n,es in pairs(doctype.ents) do
  119. text = text .. entity_tostring(n,es)
  120. end
  121. doctype.ents[name] = root
  122. return text
  123. end
  124. local function element_body(name,text)
  125. -- get the entity's children from dtd text
  126. -- name: the name of the element
  127. -- text: the list of children
  128. text = string.gsub(text,"[%s%?%*%#%+%(%)]","")
  129. text = string.gsub(text,"$","|")
  130. text = string.gsub(text,
  131. "^(.-)|",
  132. function(s)
  133. if dtd.ents[name] == nil then
  134. dtd.ents[name] = {ents={},attrs={}}
  135. end
  136. dtd.ents[name].ents[s] = true
  137. return ""
  138. end
  139. )
  140. return ''
  141. end
  142. local function attlist_body(name,text)
  143. -- get the entity's attributes from dtd text
  144. -- name: the name of the element
  145. -- text: the list of attributes
  146. text = string.gsub(text,"([%w%d_-]+) [A-Z]+ #[A-Z]+",
  147. function(s)
  148. dtd.atts[s] = true
  149. return ""
  150. end
  151. )
  152. return ''
  153. end
  154. local function dtd_body(buff)
  155. -- get the dtd's entities from dtd text
  156. -- buff: the dtd text
  157. local old_buff = buff
  158. buff = string.gsub(buff,"<!ELEMENT ([%w%d_-]+) (%b())>%s*",element_body)
  159. buff = string.gsub(buff,"<!ATTLIST ([%w%d_-]+) (.-)>%s*",attlist_body)
  160. end
  161. local function load_dtd(filename)
  162. local dtd_filename = USER_DIR .. "/dtds/" .. filename
  163. local buff = ''
  164. local wireshark_info
  165. dtd_name = nil
  166. dtd = nil
  167. for line in io.lines(dtd_filename) do
  168. buff = buff .. line
  169. end
  170. buff = string.gsub(buff ,"%s*<%!%s*","<!");
  171. buff = string.gsub(buff ,"%s*>%s*",">");
  172. buff = string.gsub(buff ,"<!%-%-(.-)%-%->"," ");
  173. buff = string.gsub(buff ,"%s+"," ");
  174. buff = string.gsub(buff ,"^%s+","");
  175. buff = string.gsub(buff,'(<%?%s*wireshark:protocol%s+.-%s*%?>)',
  176. function(s)
  177. wireshark_info = s
  178. end
  179. )
  180. buff = string.gsub(buff,"^<!DOCTYPE ([%w%d_-]+) (%b[])%s*>",
  181. function(name,body)
  182. dtd = { ents = {}, attrs = {}}
  183. dtd_name = name
  184. dtds[name] = dtd
  185. dtd_body(body)
  186. return ""
  187. end
  188. )
  189. if not dtd then
  190. dtd_body(buff)
  191. end
  192. if wireshark_info then
  193. dtd.wstag = wireshark_info
  194. end
  195. end
  196. local function load_dtds()
  197. -- loads all existing dtds in the user directory
  198. local dirname = persconffile_path("dtds")
  199. local status, dir = pcall(Dir.open,dirname,".dtd")
  200. w:set('Loading DTDs from ' .. dirname .. ' \n')
  201. if not status then
  202. w:append("Error: could not open the directory" .. dirname .. " , make sure it exists.\n")
  203. return
  204. end
  205. for dtd_filename in dir do
  206. w:append("File:" .. dtd_filename .. "\n")
  207. load_dtd(dtd_filename)
  208. end
  209. end
  210. local function dtd_window(name)
  211. return function()
  212. local wd = TextWindow.new(name .. '.dtd')
  213. wd:set(dtd_tostring(name,dtds[name]))
  214. wd:set_editable()
  215. local function save()
  216. local file = io.open(persconffile_path("dtds/") .. name .. ".dtd" ,"w")
  217. file:write(wd:get_text())
  218. file:close()
  219. end
  220. wd:add_button("Save",save)
  221. end
  222. end
  223. local function close()
  224. if li ~= nil then
  225. li:remove()
  226. li = nil
  227. end
  228. end
  229. w:set_atclose(close)
  230. -- w:add_button("Help",help)
  231. load_dtds()
  232. local li = Listener.new("frame","xml")
  233. w:append('Running')
  234. function li.packet()
  235. w:append('.')
  236. local txt = xml_fld().range:string();
  237. get_dtd_from_xml(txt)
  238. end
  239. function li.draw()
  240. for name,j in pairs(changed) do
  241. w:append("\n" .. name .. " has changed\n")
  242. if not displayed[name] then
  243. w:add_button(name,dtd_window(name))
  244. displayed[name] = true
  245. end
  246. end
  247. end
  248. retap_packets()
  249. w:append(t2s(dtds))
  250. w:append('\n')
  251. end
  252. register_menu("DTD Generator",dtd_generator,MENU_TOOLS_UNSORTED)
  253. end