require 'tap/controller'
require 'tap/mechanize/utils'
require 'tap/ubiquity/utils'
require 'uri'
module Tap
module Mechanize
# :startdoc::controller
# :startdoc::ubiquity
class Capture < Tap::Controller
include Tap::Mechanize::Utils
include Tap::Ubiquity::Utils
PREFIX = '__redirect_http_'
# Brings up the tutorial.
def index
render 'index.erb', :locals => {:captures => data.index(:data) }
end
def create(id)
data.create(:data, id) do |io|
io << YAML.dump([parse_request])
end
download(id)
end
def show(id)
response['Content-Type'] = "text/plain"
data.read(:data, id)
end
def download(id)
path = data.path(:data, id)
filename = id
filename += ".yml" if File.extname(id) == ""
response['Content-Type'] = "text/plain"
response['Content-Disposition'] = "attachment; filename=#{filename};"
data.read(:data, id)
end
def update(id="request")
path = data.path(:data, id)
requests = File.exists?(path) ? YAML.load_file(path) : []
requests << parse_request
data.create_or_update(:data, id) do |io|
io << YAML.dump(requests)
end
download(id)
end
def destroy(id)
data.destroy(:data, id)
redirect uri(:index)
end
# Brings up a tutorial teaching how to capture and resubmit forms.
def tutorial
render 'tutorial.erb'
end
def command
serve js_injection(:redirect_http)
end
def test
render 'test.erb'
end
# Say is the target of the tutorial.
def say
"
#{request.params['words']}
"
end
# Returns the redirection script.
def redirect_http
# note configs are formatted into the javascript; keys MUST be valid
# names, but values can be anything that converts to json
configs = [
[:submit_request, false],
[:keep_content, false],
[:keep_headers, false],
[:name, 'request']
]
css = render 'redirect.css'
script = render 'redirect.js', :locals => {
:redirect_action => uri(:update),
:configs => configs
}
content = render 'redirect_http.erb', :locals => {
:css => css,
:script => script,
:configs => configs
}
if request.get?
response['Content-Type'] = 'text/plain'
%Q{
#{content}
}
else
response['Content-Type'] = 'text/javascript'
%Q{
if(current = document.getElementById("#{prefix}")) {
RedirectHttp.revert();
} else {
var div = document.createElement("div");
div.id = "#{prefix}";
div.innerHTML = #{content.to_json};
document.body.insertBefore(div, document.body.firstChild);
RedirectHttp.redirect();
}
}
end
end
# Parses HTTP request
def http
if request.get?
render 'http.erb'
else
keep_content = request.params['keep_content'] == "true"
hash = {}
parse_http_request(request.params['http'], keep_content).each_pair do |key, value|
hash[key.to_s] = value
end
# remove extranous data
hash.delete('headers')
hash.delete('version')
response['Content-Type'] = "text/plain"
YAML.dump(hash)
end
end
protected
# helper for rendering... saves specification of
# :locals => {:prefix => PREFIX}
def prefix # :nodoc:
PREFIX
end
def data
server.data
end
def uri(*args)
"http://#{server.host}:#{server.port}#{super}"
end
# Returns the data recieved in the query string.
def get_params
env = request.env
if env["rack.request.query_string"] == request.query_string
env["rack.request.query_hash"]
else
env["rack.request.query_string"] = request.query_string
env["rack.request.query_hash"] =
parse_query(request.query_string)
end
end
# Returns the data recieved in the request body.
#
# This method support both application/x-www-form-urlencoded and
# multipart/form-data.
def post_params
env = request.env
if env["rack.request.form_input"].eql? env["rack.input"]
env["rack.request.form_hash"]
elsif request.form_data?
env["rack.request.form_input"] = env["rack.input"]
unless env["rack.request.form_hash"] =
Rack::Utils::Multipart.parse_multipart(env)
form_vars = env["rack.input"].read
# Fix for Safari Ajax postings that always append \0
form_vars.sub!(/\0\z/, '')
env["rack.request.form_vars"] = form_vars
env["rack.request.form_hash"] = parse_query(form_vars)
begin
env["rack.input"].rewind if env["rack.input"].respond_to?(:rewind)
rescue Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
end
end
env["rack.request.form_hash"]
else
{}
end
end
def capture_parameters # :nodoc:
get_params.update(post_params)
end
# helper to parse the request into a request hash for
# use by a Tap::Mechanize::Submit task
def parse_request
params = capture_parameters
capture_params = {}
params.each_key do |key|
if key =~ /#{prefix}/
normalize_params(capture_params, key, params.delete(key))
end
end
capture_params = capture_params[prefix]
hash = {}
parse_rack_request(request, params, capture_params['keep_content'] == "true").each_pair do |key, value|
hash[key.to_s] = value
end
action = capture_params['original_action']
hash['uri'] = case action
when /^http/
# action is an href already
action
when /^\//
# make action relative to host
action, query = action.split('?', 2)
uri = URI.parse(hash['headers']['Referer'].to_s)
uri.path = action
uri.query = query.to_s.gsub(/\s/, "+")
uri.to_s
else
# make action relative to Referer
base = File.dirname(hash['headers']['Referer'].to_s)
File.join(base, action)
end
# remove extra data
unless capture_params['keep_headers'] == 'true'
hash.delete('headers')
end
hash.delete('version')
hash
end
end
end
end