# frozen_string_literal: true
#
# Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin. If not, see .
#
require 'ronin/core/cli/command_shell'
require 'ronin/cli/printing/http'
require 'ronin/support/network/http'
module Ronin
class CLI
#
# The interactive HTTP shell for the `ronin http --shell` command.
#
class HTTPShell < Core::CLI::CommandShell
include Printing::HTTP
# The base URI.
#
# @return [URI::HTTP]
attr_reader :base_url
# The HTTP session.
#
# @return [Ronin::Support::Network::HTTP]
attr_reader :http
#
# Initializes the HTTP Shell.
#
# @param [Addressable::URI] base_url
# The base URL to connect to.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional arguments for `Ronin::Support::Network::HTTP#connect_uri`.
#
def initialize(base_url, **kwargs)
@base_url = base_url
@http = Support::Network::HTTP.connect_uri(@base_url,**kwargs)
super()
end
# The shell prompt name.
#
# @return [String]
# Returns the {#base_url} as a String.
#
def shell_name
"#{@base_url}"
end
command :cd, usage: 'PATH',
summary: 'Changes the base URL path'
#
# The `cd` shell command.
#
# @param [String] path
#
def cd(path)
@base_url.path = join(path)
end
command :get, usage: 'PATH[?QUERY] [BODY]',
summary: 'Performs a GET request'
#
# The `get` shell command.
#
# @param [String] path
#
# @param [String, nil] body
#
def get(path,body=nil)
request(:get,path, body: body)
end
command :head, usage: 'PATH[?QUERY]',
summary: 'Performs a HEAD request'
#
# The `head` shell command.
#
# @param [String] path
#
def head(path)
request(:head,path)
end
command :patch, usage: 'PATH[?QUERY] [BODY]',
summary: 'Performs a PATCH request'
#
# The `patch` shell command.
#
# @param [String] path
#
# @param [String] body
#
def patch(path,body=nil)
request(:patch,path, body: body)
end
command :post, usage: 'PATH[?QUERY] [BODY]',
summary: 'Performs a POST request'
#
# The `post` shell command.
#
# @param [String] path
#
# @param [String, nil] body
#
def post(path,body)
request(:post,path, body: body)
end
command :put, usage: 'PATH [BODY]',
summary: 'Performs a PUT request'
#
# The `patch` shell command.
#
# @param [String] path
#
# @param [String, nil] body
#
def put(path,body=nil)
request(:put,path, body: body)
end
command :copy, usage: 'PATH DEST',
summary: 'Performs a COPY request'
#
# The `copy` shell command.
#
# @param [String] path
#
# @param [String] dest
#
def copy(path,dest)
request(:copy,path, destination: dest)
end
command :delete, usage: 'PATH[?QUERY]',
summary: 'Performs a DELETE request'
#
# The `delete` shell command.
#
# @param [String] path
#
def delete(path)
request(:delete,path)
end
command :lock, usage: 'PATH[?QUERY]',
summary: 'Performs a LOCK request'
#
# The `lock` shell command.
#
# @param [String] path
#
def lock(path)
request(:lock,path)
end
command :options, usage: 'PATH[?QUERY]',
summary: 'Performs a OPTIONS request'
#
# The `options` shell command.
#
# @param [String] path
#
def options(path)
request(:options,path)
end
command :mkcol, usage: 'PATH[?QUERY]',
summary: 'Performs a MKCOL request'
#
# The `mkcol` shell command.
#
# @param [String] path
#
def mkcol(path)
request(:mkcol,path)
end
command :move, usage: 'PATH[?QUERY] DEST',
summary: 'Performs a MOVE request'
#
# The `move` shell command.
#
# @param [String] path
#
# @param [String] dest
#
def move(path,dest)
request(:move,path, destination: dest)
end
command :propfind, usage: 'PATH[?QUERY]',
summary: 'Performs a PROPFIND request'
#
# The `propfind` shell command.
#
# @param [String] path
#
def propfind(path)
request(:propfind,path)
end
command :proppatch, usage: 'PATH[?QUERY]',
summary: 'Performs a PROPPATCH request'
#
# The `proppatch` shell command.
#
# @param [String] path
#
def proppatch(path)
request(:proppatch,path)
end
command :trace, usage: 'PATH[?QUERY]',
summary: 'Performs a TRACE request'
#
# The `trace` shell command.
#
# @param [String] path
#
def trace(path)
request(:trace,path)
end
command :unlock, usage: 'PATH[?QUERY]',
summary: 'Performs a UNLOCK request'
#
# The `unlock` shell command.
#
# @param [String] path
#
def unlock(path)
request(:unlock,path)
end
command :headers, usage: '[{set | unset} NAME [VALUE]]',
summary: 'Manages the request headers'
#
# The `set_header` shell command.
#
# @param [String, nil] subcommand
# The optional sub-command (ex: `"set"` or `"unset"`).
#
# @param [String, nil] name
# The optional header name to set/unset.
#
# @param [String, nil] value
# The optional value of the header to set.
#
def headers(subcommand=nil,name=nil,value=nil)
case subcommand
when 'set'
if (name && value)
@http.headers[name] = value
else
puts "headers: must specify both NAME and VALUE arguments"
end
when 'unset'
if name
@http.headers.delete(name)
else
puts "headers: must specify NAME argument"
end
when nil
unless @http.headers.empty?
@http.headers.each do |name,value|
puts "#{colors.bold(name)}: #{value}"
end
else
puts "No request headers set"
end
else
puts "headers: unknown sub-command: #{subcommand.inspect}"
end
end
command :cookie, summary: 'Prints the Cookie value'
#
# The `cookie` shell command.
#
def cookie
if @http.cookie
puts @http.cookie
end
end
command :"set-cookie", method_name: :set_cookie,
usage: 'NAME=VALUE; ...',
summary: 'Sets the Cookie value'
#
# The `set-cookie` shell command.
#
# @param [Array] params
# The `NAME=VALUE` param pairs.
#
def set_cookie(*params)
@http.cookie = params.join(' ')
end
#
# Joins the given path with the {#base_url}.
#
# @param [String] path
# The path to join.
#
# @return [String]
# The resulting joined path.
#
def join(path)
if path.start_with?('/')
path
else
File.expand_path(File.join(@base_url.path,path))
end
end
#
# Sends an HTTP request.
#
# @param [Symbol] method
# The HTTP request method name.
#
# @param [String] path
# The path for the request.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments for
# `Ronin::Support::Network::HTTP#request`.
#
def request(method,path,**kwargs)
path = join(path)
@http.request(method,path,**kwargs) do |response|
print_response(response)
end
end
#
# Prints an HTTP response.
#
# @param [Net::HTTPResponse] response
# The HTTP response object.
#
# @see HTTPMethods#print_response
#
def print_response(response)
super(response, show_headers: true)
end
end
end
end