aboutsummaryrefslogtreecommitdiffstats
path: root/epan/wslua/dtd_gen.lua
blob: 58629bf27ea3adf68b6f3184329478b4e2c1f9da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
-- dtd_gen.lua
--
-- a DTD generator for wireshark
--
-- (c) 2006 Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
--
-- $Id:$
-- 
-- Wireshark - Network traffic analyzer
-- By Gerald Combs <gerald@wireshark.org>
-- Copyright 1998 Gerald Combs
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

if gui_enabled() then
	local xml_fld = Field.new("xml")

	local function dtd_generator()
		local displayed = {} -- whether or not a dtd is already displayed
		local dtds = {} -- the dtds
		local changed = {} -- whether or not a dtd has been modified
		local dtd -- the dtd being dealt with
		local dtd_name -- its name

		-- we'll tap onto every frame that has xml

		local ws = {} -- the windows for each dtd
		local w = TextWindow.new("DTD Generator")

		local function help()
			local wh = TextWindow.new("DTD Generator Help")
			-- XXX write help
			wh:set('DTD Generator Help\n')
		end

		local function get_dtd_from_xml(text,d,parent)
		-- obtains dtd information from xml
		--   text: xml to be parsed
		--   d: the current dtd (if any)
		--   parent: parent entity (if any)

			-- cleanup the text from useless chars
			text = string.gsub(text ,"%s*<%s*","<");
			text = string.gsub(text ,"%s*>%s*",">");
			text = string.gsub(text ,"<%-%-(.-)%-%->"," ");
			text = string.gsub(text ,"%s+"," ");

			while true do
				-- find the first tag
				local open_tag = string.match(text,"%b<>")

				if open_tag == nil then 
					-- no more tags, we're done
					return true
				end

				local name = string.match(open_tag,"[%w%d_-]+")
				local this_ent = nil

				if d == nil then
					-- there's no current dtd, this is entity is it
					d = dtds[name]

					if d == nil then
						d = {ents = {}, attrs = {}}
						dtds[name] = d
					end

					dtd = d
					dtd_name = name
				end

				this_ent = d[name]

				if this_ent == nil then
					-- no entity by this name in this dtd, create it
					this_ent = {ents = {}, attrs = {}}
					d.ents[name] = this_ent
					changed[dtd_name] = true
				end

				if parent ~= nil then
					-- add this entity to its parent
					parent.ents[name] = 1
					changed[dtd_name] = true
				end
				
				-- add the attrs to the entity
				for att in string.gmatch(open_tag, "([%w%d_-]+)%s*=") do
					if not this_ent.attrs[att] then
						changed[dtd_name] = true
						this_ent.attrs[att] = true
					end
				end

				if string.match(open_tag,"/>") then
					-- this tag is "self closed" just remove it and continue
					text = string.gsub(text,"%b<>","",1)
				else
					local close_tag_pat = "</%s*" .. name .. "%s*>"
					if not string.match(text,close_tag_pat) then return false end
					local span,left = string.match(text,"%b<>(.-)" .. close_tag_pat .. "(.*)")

					if span ~= nil then
						-- recurse to find child entities
						if not get_dtd_from_xml(span,d,this_ent) then
							return false
						end
					end

					-- continue with what's left
					text = left
				end
			end

			return true
		end

		local function entity_tostring(name,entity_data)
		-- name: the name of the entity
		-- entity_data: a table containg the entity data
		-- returns the dtd text for that entity
			local text = ''
			text = text .. '\t<!ELEMENT ' .. name .. '  (' --)
			for e,j in pairs(entity_data.ents) do
				text = text .. " " .. e .. ' |'
			end
			text = text .. " #PCDATA ) >\n"
			
			text = text .. "\t<!ATTLIST " .. name
			for a,j in pairs(entity_data.attrs) do
				text = text .. "\n\t\t" .. a .. ' CDTATA #IMPLIED'
			end
			text = text .. " >\n\n"
			
			text = string.gsub(text,"<!ATTLIST " .. name .. " >\n","")
			
			return text
		end

		local function dtd_tostring(name,doctype) 
			local text = '<? wireshark:protocol proto_name="' .. name ..'" hierarchy="yes" ?>\n\n'
			local root = doctype.ents[name]
			doctype.ents[name] = nil

			text = text .. entity_tostring(name,root)
			
			for n,es in pairs(doctype.ents) do
				text = text .. entity_tostring(n,es)
			end

			doctype.ents[name] = root

			return text
		end


		local function element_body(name,text)
		-- get the entity's children from dtd text
		--    name: the name of the element
		--    text: the list of children
			text = string.gsub(text,"[%s%?%*%#%+%(%)]","")
			text = string.gsub(text,"$","|")
			text = string.gsub(text,
							   "^(.-)|",
							   function(s)
									if dtd.ents[name] == nil then
									   dtd.ents[name] = {ents={},attrs={}}
									end
							   
									dtd.ents[name].ents[s] = true
									return ""
							   end
							   )
			return ''
		end

		local function attlist_body(name,text)
		-- get the entity's attributes from dtd text
		--    name: the name of the element
		--    text: the list of attributes
		text = string.gsub(text,"([%w%d_-]+) [A-Z]+ #[A-Z]+",
								function(s)
									dtd.atts[s] = true
									return ""
								end
								)
			return ''
		end

		local function dtd_body(buff)
		-- get the dtd's entities from dtd text
		--    buff: the dtd text

			local old_buff = buff

			buff = string.gsub(buff,"<!ELEMENT ([%w%d_-]+) (%b())>%s*",element_body)
			buff = string.gsub(buff,"<!ATTLIST ([%w%d_-]+) (.-)>%s*",attlist_body)
		end

		local function load_dtd(filename)
			local dtd_filename = USER_DIR ..  "/dtds/" .. filename
			local buff = ''
			local wireshark_info

			dtd_name = nil
			dtd = nil

			for line in io.lines(dtd_filename) do
				buff = buff .. line
			end

			buff = string.gsub(buff ,"%s*<%!%s*","<!");
			buff = string.gsub(buff ,"%s*>%s*",">");
			buff = string.gsub(buff ,"<!%-%-(.-)%-%->"," ");
			buff = string.gsub(buff ,"%s+"," ");
			buff = string.gsub(buff ,"^%s+","");


			buff = string.gsub(buff,'(<%?%s*wireshark:protocol%s+.-%s*%?>)',
							   function(s)
									wireshark_info = s
							   end
							   )

			buff = string.gsub(buff,"^<!DOCTYPE ([%w%d_-]+) (%b[])%s*>",
							   function(name,body) 
								   dtd = { ents = {}, attrs = {}}
								   dtd_name = name
								   
								   dtds[name] = dtd
								   
								   dtd_body(body)
								   
								   return ""
							   end
							   )

			if not dtd then
				dtd_body(buff)
			end
			
			if wireshark_info then
				dtd.wstag = wireshark_info
			end
		end

		local function load_dtds()
		-- loads all existing dtds in the user directory
			local dirname = persconffile_path("dtds")
			local status, dir = pcall(Dir.open,dirname,".dtd")

			 w:set('Loading DTDs from ' .. dirname .. ' \n')

			if not status then
				w:append("Error: could not open the directory" .. dirname .. " , make sure it exists.\n")
				return
			end
						 
			for dtd_filename in dir do
				w:append("File:" .. dtd_filename .. "\n")
				load_dtd(dtd_filename)
			end

		end

		local function dtd_window(name)
			return function()
				local wd = TextWindow.new(name .. '.dtd')
				wd:set(dtd_tostring(name,dtds[name]))
				wd:set_editable()

				local function save()
					local file = io.open(persconffile_path("dtds/") .. name .. ".dtd" ,"w")
					file:write(wd:get_text())
					file:close()
				end
				
				wd:add_button("Save",save)
			end
		end

		local function close()
			if li ~= nil then
				li:remove()
				li = nil
			end
		end

		w:set_atclose(close)

		-- w:add_button("Help",help)

		load_dtds()

		local li = Listener.new("frame","xml")

		w:append('Running')

		function li.packet() 
			w:append('.')
			local txt = xml_fld().range:string();
			get_dtd_from_xml(txt)
		end

		function li.draw()

			for name,j in pairs(changed) do
				w:append("\n" .. name .. " has changed\n")
				if not displayed[name] then
					w:add_button(name,dtd_window(name))
					displayed[name] = true
				end
			end
		end

		retap_packets()
		 w:append(t2s(dtds))
		w:append('\n')

	end

	register_menu("DTD Generator",dtd_generator,MENU_TOOLS)
end