if $0 == __FILE__ $:.unshift "./lib/#{ Config::CONFIG['arch'] }" $:.unshift "./lib" require 'htree' HTree::ElementContent.each { |k, v| puts "#{k} = #{v.inspect}" } exit end class MouseHole def upwink( req, res ) if res.status == 200 and req.request_uri.to_s !~ /^#{ home_url }/ if req.request_uri.path =~ /\.user\.rb$/ scrip = File.basename( req.request_uri.path ) evaluator = Evaluator.new( scrip, res.body.dup ) t = Tempfile.new( scrip ) t.write evaluator.code evaluator.script_id = t.path @temp_scripts[t.path] = [t, req.request_uri.to_s, scrip] t.close Thread.start( evaluator ) do |e| e.taint $SAFE = 4 e.evaluate end res.body = installer_pane( evaluator, "#{ home_url }mouseHole/install" ) res['content-type'] = 'text/html' res['content-length'] = res.body.length no_cache res res.setup_header elsif @conf[:rewrites_on] each_fresh_script do |path, script| next unless script.match req.request_uri next unless script.document_converter.handles_type?(res.content_type) logger.info "Executing #{script.name}" script.execute( req, res ) end res['content-length'] = res.body.length no_cache res end end end class HtmlDocumentConverter def parse_string(req, res) parse_xhtml(HTree.parse(res.body)) end def output_string(document, res) fix_doc(document) document.write(res.body = "") end def fix_doc(e) if(HTree::ElementContent[e.expanded_name] == :EMPTY) e.children.each { |x| x.delete} elsif e.children.empty? e << REXML::Text.new('') end e.each { |x| fix_doc(x) if REXML::Element === x} end def parse_xhtml( htree ) htree.each_child do |child| if child.respond_to? :qualified_name if child.qualified_name == 'html' return HTree::Doc.new( child ).to_rexml break end end end end def handles_type?(type) [ /^text\/html/, /^application\/xhtml+xml/ ].any? {|x| x === type } end end class XmlDocumentConverter def parse_string(req, res) REXML::Document.new(res.body) end def output_string(document, res) document.write(res.body = "") end def handles_type?(type) /^text\/xml/ === type end end class FeedToolsDocumentConverter def handles_type?(type) [/^text\/xml/, /^application\/xml/].any? {|x| x === type} end def parse_string(req, res) require 'feed_tools' FeedTools.feed_cache = nil feed = FeedTools::Feed.new feed.url = req.request_uri.to_s feed.feed_data_type = :xml feed.feed_data = res.body feed end def output_string(feed, res) res['content-type'] = 'application/xml+atom' res.body = feed.build_xml('atom', 1.0) end end class UserScript def document_converter s = nil; s ? @document_converter = s : (@document_converter || HtmlDocumentConverter.new) end def intercept &blk; @intercept_proc = blk; end def intercept_proc; @intercept_proc; end def execute( req, res ) return unless rewrite_proc @document = document_converter.parse_string( req, res ) return unless @document rewrite_proc[ req, res ] document_converter.output_string(document, res ) end def execute_intercept( req, path, header ) return unless intercept_proc intercept_proc[ req, path, header ] end def read_xhtml_from( uri ) HtmlDocumentConverter.new.parse_xhtml( open( uri ) { |f| HTree.parse f } ) end end module ExtraSpecialThings def core_proxy_service(req, path, header) uri = req.request_uri #~ if proxy = proxy_uri(req, nil) #~ p "Using Proxy #{proxy}" #~ proxy_host = proxy.host #~ proxy_port = proxy.port #~ if proxy.userinfo #~ credentials = "Basic " + encode64(proxy.userinfo) #~ header['proxy-authorization'] = credentials #~ end #~ end #~ http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port) http = Net::HTTP.new(uri.host, uri.port) http.start{ #~ if @config[:ProxyTimeout] #~ ################################## these issues are #~ http.open_timeout = 30 # secs # necessary (maybe bacause #~ http.read_timeout = 60 # secs # Ruby's bug, but why?) #~ ################################## #~ end case req.request_method when "GET" then response = http.get(path, header) when "POST" then response = http.post(path, req.body || "", header) when "HEAD" then response = http.head(path, header) else raise WEBrick::HTTPStatus::MethodNotAllowed, "unsupported method `#{req.request_method}'." end } end end class UserScript; include ExtraSpecialThings; end include ExtraSpecialThings def proxy_service(req, res) # Proxy Authentication proxy_auth(req, res) # Create Request-URI to send to the origin server uri = req.request_uri reqpath = uri.path.dup reqpath << "?" << uri.query if uri.query # Choose header fields to transfer header = Hash.new choose_header(req, header) set_via(header) # select upstream proxy server begin each_fresh_script do |path, script| next unless script.match req.request_uri logger.info "Executing #{script.name}" response = script.execute_intercept( req, reqpath, header ) break if response end response = core_proxy_service(req,reqpath, header) unless response rescue => err logger.debug("#{err.class}: #{err.message}") puts err.backtrace raise raise WEBrick::HTTPStatus::ServiceUnavailable, err.message end # Persistent connction requirements are mysterious for me. # So I will close the connection in every response. res['proxy-connection'] = "close" res['connection'] = "close" # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy res.status = response.code.to_i choose_header(response, res) set_cookie(response, res) set_via(res) res.body = response.body # Process contents if handler = @config[:ProxyContentHandler] handler.call(req, res) end end end #restore this so that it can go about it's business. class REXML::Element def write(writer=$stdout, indent=-1, transitive=false, ie_hack=false) #print "ID:#{indent}" writer << "<#@expanded_name" @attributes.each_attribute do |attr| writer << " " attr.write( writer, indent ) end unless @attributes.empty? if @children.empty? if transitive and indent>-1 writer << "\n" indent( writer, indent ) elsif ie_hack writer << " " end writer << "/" else if transitive and indent>-1 and !@children[0].kind_of? Text writer << "\n" indent writer, indent+1 end writer << ">" write_children( writer, indent, transitive, ie_hack ) writer << "-1 and !@children.empty? writer << "\n" indent -= 1 if next_sibling.nil? indent(writer, indent) end writer << ">" end end