require 'htree' require 'web/escape' module Web class Agent class Forms include Enumerable def initialize(html) @forms = [] parse(html) end attr_reader :forms def each @forms.each {|form| yield form } end def parse(html) form = nil doc = HTree.parse(html) doc.traverse_with_path {|elem,path| next unless path=~/\/form\z/ @forms << Form.new(elem) } end class Form def initialize(elem) @method = (elem.get_attr('method') || 'GET').upcase @action = elem.get_attr('action') @enctype = elem.get_attr('enctype') || 'application/x-www-form-urlencoded' @fields = [] parse(elem) end attr_accessor :method, :action, :enctype, :fields def parse(elem) elem.traverse_element {|e| case e.qualified_name when "input" field = input(e) @fields << field if field when "textarea" @fields << textarea(e) when "select" @fields << select(e) end } end def input(elem) type = elem.get_attr("type") name = elem.get_attr('name') value = elem.get_attr('value') case type when 'hidden' Hidden.new(name, value) when 'password' Password.new(name, value) when 'checkbox' checked = elem.get_attr('value')=~/checked/i Checkbox.new(name, value, checked) when 'radio' checked = elem.get_attr('value')=~/checked/i Radio.new(name, value, checked) when 'submit' Submit.new(name, value) when 'file' File.new(name) when 'hidden' Hidden.new(name, value) when 'image' Image.new(name, value) when 'reset' nil else Text.new(name, value) end end def textarea(elem) name = elem.get_attr('name') text = '' elem.each_child {|c| text << Web::unescapeHTML(c.raw_string) } Textarea.new(name, text) end def select(elem) name = elem.get_attr('name') multiple = elem.get_attr('multiple')=~/multiple/i ? true : false options = [] field = Select.new(name, multiple, []) elem.each_child {|c| if c.is_a?(HTree::Elem) && c.qualified_name=='option' value = c.get_attr('value') selected = c.get_attr('selected') =~ /selected/i ? true : false field.options << Select::Option.new(value, selected) end } field end def submit(agent) uri = agent.uri + @action agent.req.header['content-type'] = @enctype agent.req.form.clear if @method=='POST' @fields.each {|field| case field when Text agent.req.form.add field.name, field.value when Password agent.req.form.add field.name, field.value when Checkbox agent.req.form.add field.name, field.value if field.checked when Radio agent.req.form.add field.name, field.value if field.checked when Submit agent.req.form.add field.name, field.value when File filedata = Web::Common::FileData.new(field.name,'application/octet-stream') filedata.path = field.path agent.req.form.add field.name, filedata when Hidden agent.req.form.add field.name, field.value when Image agent.req.form.add field.name, field.value when Textarea agent.req.form.add field.name, field.text when Select field.options.each {|o| if o.checked agent.req.form.add field.name, o.value end } end } agent.post(uri) else agent.set_uri(uri) @fields.each {|field| case field when Text agent.req.query.add field.name, field.value when Password agent.req.query.add field.name, field.value when Checkbox agent.req.query.add field.name, field.value if field.checked when Radio agent.req.query.add field.name, field.value if field.checked when Submit agent.req.query.add field.name, field.value when File filedata = Web::Common::FileData.new(field.name,'application/octet-stream') filedata.path = field.path agent.req.query.add field.name, filedata when Hidden agent.req.query.add field.name, field.value when Image agent.req.query.add field.name, field.value when Textarea agent.req.query.add field.name, field.text when Select field.options.each {|o| if o.checked agent.req.query.add field.name, o.value end } end } agent.get(agent.uri.to_s) end end class Text def initialize(name,value) @name = name @value = value end attr_accessor :name, :value end class Password def initialize(name,value) @name = name @value = value end attr_accessor :name, :value end class Checkbox def initialize(name, value, checked) @name = name @value = value @checked = checked end attr_accessor :name, :value, :checked end class Radio def initialize(name, value, checked) @name = name @value = value @checked = checked end attr_accessor :name, :value end class Submit def initialize(name,value) @name = name @value = value end attr_accessor :name, :value end class File def initialize(name,path=nil) @name = name @path = nil end attr_accessor :name, :path end class Hidden def initialize(name,value) @name = name @value = value end attr_accessor :name, :value end class Image def initialize(name,value) @name = name @value = value end attr_accessor :name, :value end class Textarea def initialize(name, text='') @name = name @text = text end attr_accessor :name,:text end class Select def initialize(name, multiple=false, options=[]) @name = name @multiple = multiple @options = options end attr_accessor :name, :multiple, :options class Option def initialize(value, selected) @value = value @selected = selected end end end end # Form end # Forms end # Agent end # Web