1#!/usr/bin/ruby
2# iExploder Web Server (using webrick)
3#
4# Copyright 2010 Thomas Stromberg - All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18require 'cgi'
19require 'webrick'
20require 'optparse'
21require './iexploder.rb'
22
23include WEBrick
24
25$INSTANCE = nil
26$last_page_requested = [Time.now().to_i, 0]
27
28# Main CGI - Pass requests to iexploder
29class IEServlet < HTTPServlet::AbstractServlet
30  def do_GET(request, response)
31    ie = $INSTANCE.dup
32    ie.test_num = request.query['t'].to_i || 0
33    ie.subtest_data = request.query['s'] || nil
34    ie.random_mode = request.query['r']
35    ie.lookup_mode = request.query['l']
36    ie.claimed_browser = request.query['b'] || nil
37    ie.stop_num = request.query['x'] || nil
38    user_agent = request['User-agent'] || 'unknown'
39    raw_user_agent = user_agent.dup
40    
41    # Shorten the user-agent displayed
42    user_agent.gsub!('Mozilla/5.0', '')
43    user_agent.gsub!('X11; ', '')
44    user_agent.gsub!('Macintosh; ', '')
45    user_agent.gsub!(' U;', '')
46    user_agent.gsub!(/^ +/, '')
47    user_agent.gsub!(' (KHTML, like Gecko)', '')
48    if user_agent =~ /Chrome/
49      user_agent.gsub!(/Safari\/[\d\.]+/, '')
50    end
51    ie.browser = user_agent
52    ie.setRandomSeed()
53    # If we are a dependency image, fiddle with the headers!
54    mime_type = request.query['m']
55    headers = []
56    if mime_type
57      for (key, value) in ie.buildHeaders(mime_type)
58        headers << "#{key}[#{value.length}]"
59        response[key] = value
60      end
61      response.body = ie.buildMediaFile(mime_type)
62    else
63      response['Content-Type'] = 'text/html'
64      response.body = ie.buildPage()
65    end
66
67    details = "?t=#{ie.test_num}"
68    if ie.subtest_data
69      details << "&s=#{ie.subtest_data}"
70    end
71    if ie.random_mode
72      details << "&r=1"
73    end
74    if ie.lookup_mode
75      details << "&l=#{ie.lookup_mode}"
76    end
77    if mime_type
78      details << "&m=#{mime_type}"
79    else
80      $last_page_requested = [Time.now().to_i, request.unparsed_uri, CGI.escape(user_agent)]
81    end
82    printf("%-45.45s %s\n", details, user_agent)
83    if headers.length > 0
84      printf("%-45.45s %s\n", "Headers for #{mime_type}:", headers.join(', '))
85    end
86  end
87end
88
89
90# Simple form
91class IEForm < HTTPServlet::AbstractServlet
92  def do_GET(request, response)
93    response['Content-Type'] = 'text/html'
94    response.body = File.read("index.html")
95  end
96end
97
98class IELogo < HTTPServlet::AbstractServlet
99  def do_GET(request, response)
100    response['Content-Type'] = 'image/png'
101    response.body = File.read("media/bug.png")
102  end
103end
104
105class NoPage < HTTPServlet::AbstractServlet
106  def do_GET(request, response)
107    response.body = 'OHAI'
108  end
109end
110
111class LastPage < HTTPServlet::AbstractServlet
112  def do_GET(request, response)
113    response.body = $last_page_requested.join(' ')
114  end
115end
116
117
118def start_server(port, config_path, log_path)
119  puts "* iExploder #{$VERSION} is loading (config=#{config_path}, port=#{port})"
120  puts "=" * 80
121  $INSTANCE = IExploder.new(config_path)
122  warn_logger = Log.new($stderr, Log::WARN)
123  config = YAML::load(File.open(config_path))
124  if not log_path
125    log_path = config['access_log_path']
126  end
127  puts "- Setting up logging to #{log_path}"
128  access_log_stream = Log.new(log_path)
129  access_log = [[ access_log_stream, AccessLog::COMMON_LOG_FORMAT ]]
130  s = WEBrick::HTTPServer.new(:Port => port, :Logger => warn_logger, :AccessLog => access_log)
131  s.mount("/", IEForm)
132  s.mount("/favicon.ico", NoPage)
133  s.mount("/media/bug.png", IELogo)
134  s.mount("/iexploder.cgi", IEServlet)
135  s.mount("/last_page.cgi", LastPage)
136  ['INT', 'TERM'].each {|signal| trap(signal) { puts "SERVER SHUTDOWN: #{signal}"; s.shutdown }}
137  puts "- iExploder is at http://127.0.0.1:#{port}"
138  s.start
139  puts ""
140  puts "Goodbye! Have a fantastic day."
141end
142
143
144
145if $0 == __FILE__
146  options = {
147    :port => 3100,
148    :config_path => 'config.yaml',
149    :log_path => nil
150  }
151
152  optparse = OptionParser.new do|opts|
153    opts.banner = "Usage: webserver.rb [options]"
154    opts.on( '-p', '--port NUM', 'Listen on TCP port NUM' ) { |port| options[:port] = port }
155    opts.on( '-c', '--config PATH', 'Use PATH for configuration file' ) { |path| options[:config_path] = path }
156    opts.on( '-l', '--log PATH', 'Use PATH for log file' ) { |path|  options[:log_path] = path }
157    opts.on( '-h', '--help', 'Display this screen' ) { puts opts; exit }
158  end
159  optparse.parse!
160  start_server(options[:port], options[:config_path], options[:log_path])
161end
162