248 lines
7.7 KiB
Ruby
Executable File
248 lines
7.7 KiB
Ruby
Executable File
#
|
|
# File:: web.rb
|
|
# Description:: Web shell
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# Date:: 28 February 2009
|
|
#
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Uses
|
|
#----------------------------------------------------------------------------
|
|
require 'builder/shell/command'
|
|
require 'builder/shell/shell'
|
|
require 'os/path'
|
|
require 'erb'
|
|
require 'webrick'
|
|
require 'webrick/httpservlet/erbhandler.rb'
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Implementation
|
|
#----------------------------------------------------------------------------
|
|
module Pipeline
|
|
module Builder
|
|
module Shell
|
|
|
|
#
|
|
# == Description
|
|
# Custom HTTP request/response class so we can spit out ERB pages and
|
|
# process POST forms to enqueue commands onto the server.
|
|
#
|
|
# It is expected that HTML form submit buttons are called the name of
|
|
# the command to execute and the value set to any parameters to be sent
|
|
# to that command.
|
|
#
|
|
# === References
|
|
# http://microjet.ath.cx/webrickguide/html/
|
|
# c:\ruby\lib\ruby\1.8\webrick\httpservlet\erbhandler.rb
|
|
#
|
|
# === Example Usage
|
|
# WEBrick::HTTPServlet::FileHandler.add_handler( 'rhtml', WebShellServlet )
|
|
#
|
|
module WebShellServlet
|
|
|
|
#
|
|
# Respond to HTTP GET requests. We steal the ERBHandler's do_GET
|
|
# method as we want to use ERB templates for ease.
|
|
#
|
|
def do_GET( req, resp )
|
|
unless defined?( ERB )
|
|
@logger.warn( "#{self.class}: ERB not defined." )
|
|
throw HTTPStatus::Forbidden, "WebShellServlet cannot work."
|
|
end
|
|
begin
|
|
filename = OS::Path::combine( Dir::pwd, req.path )
|
|
data = nil
|
|
if ( File::exists?( filename ) && File::file?( filename ) ) then
|
|
data = File::open( filename, 'r' ) do |io| io.read; end
|
|
else
|
|
# Default to @web_page
|
|
filename = @web_page
|
|
data = File::open( @web_page, 'r' ) do |io| io.read; end
|
|
end
|
|
# Process everything with ERB
|
|
resp.body = evaluate( ERB.new( data ), req, resp )
|
|
resp['Content-Type'] = WEBrick::HTTPUtils::mime_type( filename, @server.config[:MimeTypes] )
|
|
rescue StandardError => ex
|
|
puts "Ex: #{ex.message}"
|
|
ex.backtrace.each do |m| puts m; end
|
|
throw
|
|
rescue Exception => ex
|
|
@logger.error( ex )
|
|
throw HTTPStatus::InternalServerError, ex.message
|
|
end
|
|
end
|
|
|
|
#
|
|
# Respond to HTTP POST requests. We steal the ERBHandler's do_GET
|
|
# method as we want to use ERB templates for ease.
|
|
#
|
|
# We differ from the GET request processing by processing form
|
|
# data.
|
|
#
|
|
def do_POST( req, resp )
|
|
unless defined?( ERB )
|
|
@logger.warn( "#{self.class}: ERB not defined." )
|
|
throw HTTPStatus::Forbidden, "WebShellServlet cannot work."
|
|
end
|
|
|
|
puts "POST body: #{req.body}"
|
|
cmd = CommandInst::from_POST( req.body, @@commands )
|
|
enqueue_command( cmd, WebShell::HOST, WebShell::USER )
|
|
|
|
begin
|
|
filename = OS::Path::combine( Dir::pwd, req.path )
|
|
data = nil
|
|
if ( File::exists?( filename ) && File::file?( filename ) ) then
|
|
data = File::open( filename, 'r' ) do |io| io.read; end
|
|
else
|
|
# Default to @web_page
|
|
filename = @web_page
|
|
data = File::open( @web_page, 'r' ) do |io| io.read; end
|
|
end
|
|
# Process everything with ERB
|
|
resp.body = evaluate( ERB.new( data ), req, resp )
|
|
resp['Content-Type'] = WEBrick::HTTPUtils::mime_type( filename, @server.config[:MimeTypes] )
|
|
rescue StandardError => ex
|
|
puts "Ex: #{ex.message}"
|
|
ex.backtrace.each do |m| puts m; end
|
|
throw
|
|
rescue Exception => ex
|
|
@logger.error( ex )
|
|
throw HTTPStatus::InternalServerError, ex.message
|
|
end
|
|
end
|
|
|
|
#--------------------------------------------------------------------
|
|
# Class Methods
|
|
#--------------------------------------------------------------------
|
|
|
|
#
|
|
# Override self.get_instance to reload this file for each request.
|
|
# This saves us restarting the server/builder during testing.
|
|
#
|
|
def WebShellServlet::get_instance( config, *options )
|
|
load( __FILE__ )
|
|
WebShellServlet.new( config, *options )
|
|
end
|
|
|
|
#
|
|
# To parse POST String objects into CommandInst objects to enqueue
|
|
# we need to validate against an Array of Command objects. We store
|
|
# that here.
|
|
#
|
|
def WebShellServlet::set_commands( commands )
|
|
throw ArgumentError.new( "Invalid Hash of valid commands (#{commands.class})." ) \
|
|
unless ( commands.is_a?( Hash ) )
|
|
@@commands = commands
|
|
end
|
|
|
|
#--------------------------------------------------------------------
|
|
# Private Methods
|
|
#--------------------------------------------------------------------
|
|
private
|
|
def evaluate( erb, servlet_request, servlet_response )
|
|
Module.new.module_eval do
|
|
meta_vars = servlet_request.meta_vars
|
|
query = servlet_request.query
|
|
erb.result( binding )
|
|
end
|
|
end
|
|
end
|
|
|
|
#
|
|
# == Description
|
|
# Web implementation of the Builder Shell. This shell creates an
|
|
# internal Webrick HTTP server and allows users to send commands to
|
|
# the builder application.
|
|
#
|
|
# === Example Usage
|
|
#
|
|
# commands = [
|
|
# Command.new( 'sync', Command::NO_SHORTCUTS, 'Sync special directory.', [ Param.new( 'dir', String ) ], sync_proc ),
|
|
# Command.new( 'enable', [ 'e' ], 'Enable internal feature.', [ Param.new( 'flag', String ) ], enable_proc ),
|
|
# Command.new( 'disable', [ 'd' ], 'Disable internal feature.', [ Param.new( 'flag', String ) ], disable_proc ),
|
|
# Command.new( 'status', [ 's' ], 'Display status information.', Param::NONE, status_proc )
|
|
# ]
|
|
# shell = Console.new( commands )
|
|
# server = WebShell.new( commands )
|
|
# ...
|
|
# server.start( )
|
|
# # Start up our interactive shell in calling thread.
|
|
# shell.start( )
|
|
#
|
|
class WebShell < ShellBase
|
|
include WebShellServlet
|
|
|
|
DEFAULT_WEB_PORT = 81
|
|
HOST = ENV['COMPUTERNAME']
|
|
USER = ENV['USERNAME']
|
|
attr_reader :active
|
|
attr_reader :web_port
|
|
attr_reader :web_page
|
|
|
|
def initialize( commands,
|
|
web_port = DEFAULT_WEB_PORT,
|
|
web_page = './index.rhtml',
|
|
server = '127.0.0.1',
|
|
port = CommandQueue::DEFAULT_PORT )
|
|
super( commands, server, port )
|
|
@web_port = web_port
|
|
@web_page = File::expand_path( web_page )
|
|
@active = false
|
|
WebShellServlet::set_commands( @commands )
|
|
end
|
|
|
|
#
|
|
# Start the HTTP server and web UI so we can display status
|
|
# information.
|
|
#
|
|
def start( &block )
|
|
@active = true
|
|
|
|
mime_file = OS::Path::combine( OS::Path::get_directory( __FILE__ ), 'mime.types' )
|
|
@mime_types = WEBrick::HTTPUtils::load_mime_types( mime_file )
|
|
@mime_types.store( 'rhtml', 'text/html' )
|
|
|
|
Thread.new() do
|
|
Thread.current[:name] = THREAD_NAME
|
|
begin
|
|
# Start Webrick HTTP Server
|
|
# WEBrick::HTTPServlet::FileHandler.add_handler( 'rhtml', WebShellServlet )
|
|
@server = WEBrick::HTTPServer.new(
|
|
:Port => @web_port,
|
|
:DocumentRoot => @web_page,
|
|
:MimeTypes => @mime_types
|
|
)
|
|
@server.mount_proc( '/' ) do |req, resp|
|
|
case req.request_method
|
|
when 'GET'
|
|
do_GET( req, resp )
|
|
when 'POST'
|
|
do_POST( req, resp )
|
|
end
|
|
end
|
|
trap( 'INT' ) do @server.shutdown() end
|
|
|
|
@server.start( )
|
|
puts "WEBrick started."
|
|
rescue Exception => ex
|
|
puts "WebShell Exception: #{ex.message}"
|
|
ex.backtrace.each do |msg| puts msg; end
|
|
end
|
|
end
|
|
end
|
|
|
|
#--------------------------------------------------------------------
|
|
# Private
|
|
#--------------------------------------------------------------------
|
|
private
|
|
THREAD_NAME = '__BuilderFramework_WebShell'
|
|
end
|
|
|
|
end # Shell module
|
|
end # Builder module
|
|
end # Pipeline module
|
|
|
|
# web.rb
|