wwmd 0.2.20.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|