wwmd 0.2.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +38 -0
- data/README.rdoc +87 -0
- data/Rakefile +33 -0
- data/examples/config_example.yaml +24 -0
- data/examples/wwmd_example.rb +73 -0
- data/lib/wwmd.rb +84 -0
- data/lib/wwmd/class_extensions.rb +4 -0
- data/lib/wwmd/class_extensions/extensions_base.rb +251 -0
- data/lib/wwmd/class_extensions/extensions_encoding.rb +79 -0
- data/lib/wwmd/class_extensions/extensions_external.rb +18 -0
- data/lib/wwmd/class_extensions/extensions_nilclass.rb +11 -0
- data/lib/wwmd/class_extensions/extensions_rbkb.rb +193 -0
- data/lib/wwmd/class_extensions/mixins_string_encoding.rb +40 -0
- data/lib/wwmd/guid.rb +155 -0
- data/lib/wwmd/page.rb +3 -0
- data/lib/wwmd/page/_fa.old +302 -0
- data/lib/wwmd/page/auth.rb +17 -0
- data/lib/wwmd/page/constants.rb +63 -0
- data/lib/wwmd/page/form.rb +99 -0
- data/lib/wwmd/page/form_array.rb +304 -0
- data/lib/wwmd/page/headers.rb +118 -0
- data/lib/wwmd/page/helpers.rb +41 -0
- data/lib/wwmd/page/html2text_hpricot.rb +76 -0
- data/lib/wwmd/page/html2text_nokogiri.rb +42 -0
- data/lib/wwmd/page/inputs.rb +47 -0
- data/lib/wwmd/page/irb_helpers.rb +114 -0
- data/lib/wwmd/page/page.rb +257 -0
- data/lib/wwmd/page/parsing_convenience.rb +98 -0
- data/lib/wwmd/page/reporting_helpers.rb +89 -0
- data/lib/wwmd/page/scrape.rb +196 -0
- data/lib/wwmd/page/spider.rb +127 -0
- data/lib/wwmd/urlparse.rb +125 -0
- data/lib/wwmd/viewstate.rb +17 -0
- data/lib/wwmd/viewstate/viewstate.rb +101 -0
- data/lib/wwmd/viewstate/viewstate_deserializer_methods.rb +217 -0
- data/lib/wwmd/viewstate/viewstate_from_xml.rb +129 -0
- data/lib/wwmd/viewstate/viewstate_types.rb +51 -0
- data/lib/wwmd/viewstate/viewstate_utils.rb +164 -0
- data/lib/wwmd/viewstate/viewstate_yaml.rb +25 -0
- data/lib/wwmd/viewstate/vs_stubs.rb +22 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_array.rb +38 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_binary_serialized.rb +30 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_hashtable.rb +42 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_hybrid_dict.rb +42 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_indexed_string.rb +6 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_indexed_string_ref.rb +24 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_int_enum.rb +27 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_list.rb +34 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_pair.rb +29 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_read_types.rb +11 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_read_value.rb +35 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_sparse_array.rb +58 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string.rb +33 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string_array.rb +39 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_string_formatted.rb +32 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_stub_helpers.rb +37 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_triplet.rb +31 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_type.rb +23 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_unit.rb +30 -0
- data/lib/wwmd/viewstate/vs_stubs/vs_value.rb +35 -0
- data/lib/wwmd/wwmd_config.rb +52 -0
- data/lib/wwmd/wwmd_puts.rb +9 -0
- data/lib/wwmd/wwmd_utils.rb +28 -0
- data/spec/README +3 -0
- data/spec/form_array.spec +49 -0
- data/spec/spider_csrf_test.spec +28 -0
- data/spec/urlparse_test.spec +101 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +222 -0
data/lib/wwmd/page.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
This is a weird kind of data structure for no other reason than
|
3
|
+
I wanted to keep the form inputs in order when they come in.
|
4
|
+
|
5
|
+
Accessing this either as a hash or an array (but => won't work)
|
6
|
+
|
7
|
+
Some of the methods in here are kept for backward compat before the refactor
|
8
|
+
and now everything in this array should be accessed with []= and []
|
9
|
+
|
10
|
+
Set :action and take a block. Page#submit_form should take this and do the
|
11
|
+
right thing.
|
12
|
+
=end
|
13
|
+
|
14
|
+
module WWMD
|
15
|
+
class FormArray < Array
|
16
|
+
attr_accessor :action
|
17
|
+
attr_accessor :type
|
18
|
+
attr_accessor :delimiter
|
19
|
+
attr_accessor :equals
|
20
|
+
|
21
|
+
def initialize(fields=nil,action=nil,&block)
|
22
|
+
set_fields(fields)
|
23
|
+
@delimiter = "&"
|
24
|
+
@equals = "="
|
25
|
+
@action = action
|
26
|
+
instance_eval(&block) if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_fields(fields=nil)
|
30
|
+
return nil if fields.nil?
|
31
|
+
# this first one is an array of field objects
|
32
|
+
if fields.class == Array
|
33
|
+
fields.each do |f|
|
34
|
+
name = f['name']
|
35
|
+
if self.name_exists(name)
|
36
|
+
if f['type'] == "hidden"
|
37
|
+
self.set name,f.get_value
|
38
|
+
elsif f['type'] == "checkbox" and f.to_html.grep(/checked/) != ''
|
39
|
+
self[name] = f.get_value
|
40
|
+
end
|
41
|
+
else
|
42
|
+
self << [ f['name'],f.get_value ]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
elsif fields.class == Hash
|
46
|
+
fields.each_pair { |k,v| self[k] = v }
|
47
|
+
elsif fields.class == String
|
48
|
+
fields.split(@delimiter).each do |f|
|
49
|
+
k,v = f.split(@equals,2)
|
50
|
+
self[k] = v
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# "deep enough" copy of this object to make it a real copy
|
56
|
+
# instead of references to the arrays that already exist
|
57
|
+
def clone
|
58
|
+
ret = self.class.new
|
59
|
+
self.each { |r| ret << r.clone }
|
60
|
+
ret.action = self.action
|
61
|
+
return ret
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear
|
65
|
+
self.delete_if { |x| true }
|
66
|
+
end
|
67
|
+
|
68
|
+
# check if the passed name exists in the form
|
69
|
+
def include?(key)
|
70
|
+
self.map { |x| x.first }.flatten.include?(key)
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :name_exists, :include?#:nodoc:
|
74
|
+
alias_method :name_exists?, :include?#:nodoc:
|
75
|
+
alias_method :has_key?, :include?#:nodoc:
|
76
|
+
|
77
|
+
# add key/value pairs to form
|
78
|
+
def add(key,value)
|
79
|
+
self << [key,value]
|
80
|
+
end
|
81
|
+
|
82
|
+
# key = Fixnum set value at index key
|
83
|
+
# key = String find key named string and set value
|
84
|
+
def set_value!(key,value)
|
85
|
+
if key.class == Fixnum
|
86
|
+
self[key][1] = value
|
87
|
+
return [self[key][0], value]
|
88
|
+
end
|
89
|
+
self.each_index do |i|
|
90
|
+
if self[i][0] == key
|
91
|
+
self[i] = [key,value]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return [key,value]
|
95
|
+
end
|
96
|
+
|
97
|
+
# get a value using its index
|
98
|
+
# override Array#[]
|
99
|
+
alias_method :old_get, :[]#:nodoc:
|
100
|
+
def [](*args)
|
101
|
+
if args.first.class == Fixnum
|
102
|
+
self.old_get(args.first)
|
103
|
+
else
|
104
|
+
self.get_value(args.first)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
alias_method :old_set, :[]=#:nodoc:
|
109
|
+
# set a key using its index, array key or add using a new key i.e.:
|
110
|
+
# if setting:
|
111
|
+
# form = [['key','value'],['foo','bar']]
|
112
|
+
# form[0] = ["replacekey","newalue"]
|
113
|
+
# form["replacekey"] = "newervalue"
|
114
|
+
# if adding:
|
115
|
+
# form["newkey"] = "value"
|
116
|
+
#
|
117
|
+
def []=(*args)
|
118
|
+
key,value = args
|
119
|
+
if args.first.kind_of?(Fixnum)
|
120
|
+
return self.old_set(*args)
|
121
|
+
elsif self.has_key?(key)
|
122
|
+
return self.set_value(key,value)
|
123
|
+
else
|
124
|
+
return self.add(key,value)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
alias_method :set_value, :set_value!
|
129
|
+
alias_method :set, :set_value!
|
130
|
+
|
131
|
+
def get_value(key)
|
132
|
+
if key.class == Fixnum
|
133
|
+
return self[key][1]
|
134
|
+
end
|
135
|
+
self.each_index do |i|
|
136
|
+
if self[i][0] == key
|
137
|
+
return self[i][1]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
|
143
|
+
alias_method :get, :get_value
|
144
|
+
|
145
|
+
def keys
|
146
|
+
self.map { |k,v| k }
|
147
|
+
end
|
148
|
+
|
149
|
+
def setall!(value)
|
150
|
+
self.each_index { |i| self.set_value!(i,value) }
|
151
|
+
end
|
152
|
+
|
153
|
+
alias_method :setall, :setall!#:nodoc:
|
154
|
+
alias_method :set_all!, :setall!#:nodoc:
|
155
|
+
alias_method :set_all, :setall!#:nodoc:
|
156
|
+
|
157
|
+
# delete all key = value pairs from self where key = key
|
158
|
+
def delete_key(key)
|
159
|
+
self.reject! { |x,y| x == key }
|
160
|
+
end
|
161
|
+
|
162
|
+
alias_method :delete_keys!, :delete_key #:nodoc:
|
163
|
+
alias_method :delete_key!, :delete_key #:nodoc:
|
164
|
+
|
165
|
+
# escape form keys in place
|
166
|
+
def escape_keys!(reg=WWMD::ESCAPE[:url])
|
167
|
+
return nil if reg == :none
|
168
|
+
self.map! { |x,y| [x.escape(reg),y] }
|
169
|
+
end
|
170
|
+
|
171
|
+
# unescape form keys in place
|
172
|
+
def unescape_keys!(reg=WWMD::ESCAPE[:url])
|
173
|
+
return nil if reg == :none
|
174
|
+
self.map! { |x,y| [x.unescape,y] }
|
175
|
+
end
|
176
|
+
|
177
|
+
# escape form values in place
|
178
|
+
def escape_all!(reg=WWMD::ESCAPE[:url])
|
179
|
+
return nil if reg == :none
|
180
|
+
self.map! { |x,y| [x,y.escape(reg)] }
|
181
|
+
end
|
182
|
+
|
183
|
+
alias_method :escape_all, :escape_all!#:nodoc:
|
184
|
+
|
185
|
+
# unescape all form values in place
|
186
|
+
def unescape_all!
|
187
|
+
self.map! { |x,y| [x,y.unescape] }
|
188
|
+
end
|
189
|
+
|
190
|
+
alias_method :unescape_all, :unescape_all!#:nodoc:
|
191
|
+
|
192
|
+
# remove form elements with null values
|
193
|
+
def remove_nulls!
|
194
|
+
self.delete_if { |x| x[1].to_s.empty? || x[1].nil? }
|
195
|
+
end
|
196
|
+
|
197
|
+
alias_method :squeeze!, :remove_nulls!
|
198
|
+
|
199
|
+
# remove form elements with null keys (for housekeeping returns)
|
200
|
+
def remove_null_keys!
|
201
|
+
self.delete_if { |x,y| x.to_s.empty? || x.nil? }
|
202
|
+
end
|
203
|
+
|
204
|
+
alias_method :squeeze_keys!, :remove_null_keys!
|
205
|
+
|
206
|
+
## viewstate
|
207
|
+
|
208
|
+
# clear viewstate variables
|
209
|
+
def clear_viewstate
|
210
|
+
self.each { |k,v|
|
211
|
+
self[k] = "" if k =~ /^__/
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
# remove viewstate variables
|
216
|
+
def rm_viewstate
|
217
|
+
# my least favorite ruby idiom
|
218
|
+
self.replace(self.map { |k,v| [k,v] if not k =~ /^__/ }.reject { |x| x.nil? })
|
219
|
+
end
|
220
|
+
|
221
|
+
alias_method :extend!, :add #:nodoc (this is here for backward compat)
|
222
|
+
|
223
|
+
# add viewstate stuff
|
224
|
+
def add_viewstate#:nodoc:
|
225
|
+
self.insert(0,[ "__VIEWSTATE","" ])
|
226
|
+
self.insert(0,[ "__EVENTARGUMENT","" ])
|
227
|
+
self.insert(0,[ "__EVENTTARGET","" ])
|
228
|
+
self.insert(0,[ "__EVENTVALIDATION","" ])
|
229
|
+
return nil
|
230
|
+
end
|
231
|
+
|
232
|
+
## conversions
|
233
|
+
|
234
|
+
# convert form into a post parameters string
|
235
|
+
def to_post
|
236
|
+
ret = []
|
237
|
+
self.each do |i|
|
238
|
+
ret << i.join(@equals)
|
239
|
+
end
|
240
|
+
ret.join(@delimiter)
|
241
|
+
end
|
242
|
+
|
243
|
+
# convert form into a get parameters string
|
244
|
+
#
|
245
|
+
# pass me a base to get a full url to pass to Page.get
|
246
|
+
def to_get(base="")
|
247
|
+
return base if self.empty?
|
248
|
+
ret = []
|
249
|
+
self.each do |i|
|
250
|
+
ret << i.join(@equals)
|
251
|
+
end
|
252
|
+
ret = ret.join(@delimiter)
|
253
|
+
return base.to_s.clip + "?" + ret.to_s
|
254
|
+
end
|
255
|
+
|
256
|
+
## parsing convenience
|
257
|
+
|
258
|
+
# dump a web page containing a csrf example of the current FormArray
|
259
|
+
def to_csrf(action=nil,unescval=false)
|
260
|
+
action = self.action if not action
|
261
|
+
ret = ""
|
262
|
+
ret << "<html><body>\n"
|
263
|
+
ret << "<form method='post' id='wwmdtest' name='wwmdtest' action='#{action}'>\n"
|
264
|
+
self.each do |key,val|
|
265
|
+
val = val.unescape.gsub(/'/) { %q[\'] } if unescval
|
266
|
+
ret << "<input name='#{key.to_s.unescape}' type='hidden' value='#{val.to_s.unescape}' />\n"
|
267
|
+
end
|
268
|
+
ret << "</form>\n"
|
269
|
+
ret << "<script>document.wwmdtest.submit()</script>\n"
|
270
|
+
ret << "</body></html>\n"
|
271
|
+
return ret
|
272
|
+
end
|
273
|
+
|
274
|
+
# add markers for burp intruder to form
|
275
|
+
def burpify(all=true) #:nodoc:
|
276
|
+
ret = self.clone
|
277
|
+
ret.each_index do |i|
|
278
|
+
next if ret[i][0] =~ /^__/
|
279
|
+
# ret.set_value!(i,"#{ret.get_value(i)}" + "\302\247" + "\302\247")
|
280
|
+
if all
|
281
|
+
ret.set_value!(i,"\244" + "#{ret.get_value(i)}" + "\244")
|
282
|
+
else
|
283
|
+
ret.set_value!(i,"#{ret.get_value(i)}" + "\244" + "\244")
|
284
|
+
end
|
285
|
+
end
|
286
|
+
ret.to_post.pbcopy
|
287
|
+
return ret
|
288
|
+
end
|
289
|
+
|
290
|
+
# return md5 hash of sorted list of keys
|
291
|
+
def fingerprint
|
292
|
+
return (self.action.to_s + self.map { |k,v| k }.sort.to_s).md5
|
293
|
+
end
|
294
|
+
alias_method :fp, :fingerprint #:nodoc:
|
295
|
+
|
296
|
+
def from_array(arr)
|
297
|
+
self.clear
|
298
|
+
arr.each { |k,v| self[k] = v }
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module WWMD
|
2
|
+
class Page
|
3
|
+
|
4
|
+
# does this request have an authenticate header?
|
5
|
+
def auth?
|
6
|
+
return false if self.code != 401
|
7
|
+
count = 0
|
8
|
+
self.header_data.each do |i|
|
9
|
+
if i[0] =~ /www-authenticate/i
|
10
|
+
count += 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
return (count > 0)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module WWMD
|
2
|
+
XSSFISH = "<;'\"}()[]>{"
|
3
|
+
|
4
|
+
DEFAULTS = {
|
5
|
+
:base_url => "",
|
6
|
+
:use_auth => true,
|
7
|
+
:enable_cookies => true,
|
8
|
+
:cookiejar => "./__cookiejar",
|
9
|
+
:follow_location => true,
|
10
|
+
:max_redirects => 20,
|
11
|
+
:use_proxy => false,
|
12
|
+
:debug => false,
|
13
|
+
:scrape_warn => true,
|
14
|
+
:parse => true,
|
15
|
+
:timeout => 20,
|
16
|
+
}
|
17
|
+
|
18
|
+
ESCAPE = {
|
19
|
+
:url => /[^a-zA-Z0-9\-_%]/,
|
20
|
+
:nalnum => /[^a-zA-Z0-9]/,
|
21
|
+
:xss => /[^a-zA-Z0-9=?()']/,
|
22
|
+
:ltgt => /[<>]/,
|
23
|
+
:all => /.*/,
|
24
|
+
# :b64 => /[=+\/]/,
|
25
|
+
:b64 => /[^a-zA-Z0-9]/,
|
26
|
+
:none => :none,
|
27
|
+
:default => :default,
|
28
|
+
}
|
29
|
+
|
30
|
+
UA = {
|
31
|
+
:mozilla => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16",
|
32
|
+
:moz3 => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.0.1) Gecko/2008070206 Firefox/3.0.1",
|
33
|
+
:ie6 => "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
|
34
|
+
:ie7 => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
|
35
|
+
:ie8 => "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)",
|
36
|
+
:opera => "Opera/9.20 (Windows NT 6.0; U; en)",
|
37
|
+
:safari => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_4_11; en) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.22",
|
38
|
+
:safari4 => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Safari/530.17",
|
39
|
+
:wwmd => "Mozilla/5.0 (compatible; WWMD #{WWMD::VERSION}; o_hai)"
|
40
|
+
}
|
41
|
+
|
42
|
+
DEFAULT_HEADERS = {
|
43
|
+
"User-Agent" => UA[:wwmd],
|
44
|
+
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
45
|
+
"Accept-Language" => "en-US,en;q=0.8,en-au;q=0.6,en-us;q=0.4,en;q=0.2",
|
46
|
+
"Accept-Encoding" => "gzip,deflate",
|
47
|
+
"Accept-Charset" => "SO-8859-1,utf-8;q=0.7,*;q=0.7",
|
48
|
+
"Keep-Alive" => "300",
|
49
|
+
"Connection" => "keep-alive",
|
50
|
+
}
|
51
|
+
|
52
|
+
HEADERS = {
|
53
|
+
:default => nil,
|
54
|
+
:utf7 => {
|
55
|
+
"Content-Type" => "application/x-www-form-urlencoded;charset=UTF-7",
|
56
|
+
"Content-Transfer-Encoding" => "7bit",
|
57
|
+
},
|
58
|
+
:ajax => {
|
59
|
+
"X-Requested-With" => "XMLHttpRequest",
|
60
|
+
"X-Prototype-Version" => "1.5.0",
|
61
|
+
},
|
62
|
+
}
|
63
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
=end
|
3
|
+
module WWMD
|
4
|
+
# == original author of hpricot_form
|
5
|
+
#
|
6
|
+
# Chew Choon Keat <choonkeat at gmail>
|
7
|
+
# http://blog.yanime.org/
|
8
|
+
# 19 July 2006
|
9
|
+
#
|
10
|
+
# updated by mtracy at matasano.com for use with Nokogiri and WWMD
|
11
|
+
#
|
12
|
+
class Form
|
13
|
+
attr_accessor :hdoc
|
14
|
+
attr_accessor :fields
|
15
|
+
attr_accessor :formtag
|
16
|
+
|
17
|
+
def initialize(doc)
|
18
|
+
@hdoc = doc
|
19
|
+
@formtag = @hdoc.search("//form")
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(*args)
|
23
|
+
hdoc.send(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :old_fields, :fields
|
27
|
+
def fields
|
28
|
+
if PARSER == :nokogiri
|
29
|
+
@fields ||= (hdoc.search(".//input[@name]",".//select[@name]",".//textarea")).map { |x| Field.new(x) }
|
30
|
+
else
|
31
|
+
@fields ||= (hdoc.search("//input[@name]") + hdoc.search("//select[@name]") + hdoc.search("//textarea")).map { |x| Field.new(x) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def field_names
|
36
|
+
fields.map { |x| x.get_attribute("name") }
|
37
|
+
end
|
38
|
+
|
39
|
+
def action
|
40
|
+
return self.get_attribute("action")
|
41
|
+
end
|
42
|
+
|
43
|
+
def type
|
44
|
+
return self.get_attribute("method")
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class Field < Form
|
50
|
+
def value
|
51
|
+
self._value.nil? ? self.get_attribute("value") : self._value
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method :get_value, :value #:nodoc:
|
55
|
+
alias_method :fvalue, :value #:nodoc:
|
56
|
+
|
57
|
+
def fname
|
58
|
+
self.get_attribute('name')
|
59
|
+
end
|
60
|
+
|
61
|
+
def ftype
|
62
|
+
self.get_attribute('type')
|
63
|
+
end
|
64
|
+
|
65
|
+
def _value
|
66
|
+
# selection (array)
|
67
|
+
if PARSER == :nokogiri
|
68
|
+
ret = hdoc.search(".//option[@selected]").collect { |x| x.get_attribute("value") }
|
69
|
+
else
|
70
|
+
ret = hdoc.search("//option[@selected]").collect { |x| x.get_attribute("value") }
|
71
|
+
end
|
72
|
+
case ret.size
|
73
|
+
when 0
|
74
|
+
if name == "textarea"
|
75
|
+
if PARSER == :nokogiri
|
76
|
+
hdoc.text
|
77
|
+
else
|
78
|
+
hdoc.innerHTML
|
79
|
+
end
|
80
|
+
else
|
81
|
+
hdoc.get_attribute("value") if (hdoc.get_attribute("checked") || !hdoc.get_attribute("type") =~ /radio|checkbox/)
|
82
|
+
end
|
83
|
+
when 1
|
84
|
+
ret.first
|
85
|
+
else
|
86
|
+
ret
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_arr
|
91
|
+
return [self.name, self.ftype, self.fname, self.fvalue]
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_text
|
95
|
+
return "tag=#{self.name} type=#{self.ftype} name=#{self.fname} value=#{self.fvalue}"
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|