Vendor mechanize gem outside Gemfile

We only need a single file and it avoids pulling in e.g. `nokogiri`
which is vulnerable.

Allow updating it using `brew vendor-gems`.
This commit is contained in:
Douglas Eichelberger 2023-04-24 10:32:10 +01:00 committed by Mike McQuaid
parent 763c41f006
commit f52cbbb9da
No known key found for this signature in database
GPG Key ID: 3338A31AFDB1D829
82 changed files with 86 additions and 12537 deletions

View File

@ -71,9 +71,9 @@ jobs:
run: |
if [[ "${GITHUB_EVENT_NAME}" == "pull_request_target" || "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]
then
brew vendor-gems
brew vendor-gems --non-bundler-gems
else
brew vendor-gems --no-commit
brew vendor-gems --non-bundler-gems --no-commit
fi
- name: Update RBI files

50
.gitignore vendored
View File

@ -75,21 +75,30 @@
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/utility/
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/*/*.jar
**/vendor/bundle/ruby/*/gems/i18n-*/lib/i18n/tests*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/*.rb
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/http/agent.rb
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/http/*auth*.rb
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/c*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/d*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/e*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/f*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/h*.rb
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/i*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/p*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/r*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/t*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/u*
**/vendor/bundle/ruby/*/gems/mechanize-*/lib/mechanize/x*
**/vendor/bundle/ruby/*/gems/thread_safe-*/lib/thread_safe/util
**/vendor/gems/mechanize-*/.*
**/vendor/gems/mechanize-*/*.md
**/vendor/gems/mechanize-*/*.rdoc
**/vendor/gems/mechanize-*/*.gemspec
**/vendor/gems/mechanize-*/Gemfile
**/vendor/gems/mechanize-*/Rakefile
**/vendor/gems/mechanize-*/examples/
**/vendor/gems/mechanize-*/lib/*.rb
**/vendor/gems/mechanize-*/lib/*.rb
**/vendor/gems/mechanize-*/lib/mechanize/http/agent.rb
**/vendor/gems/mechanize-*/lib/mechanize/http/*auth*.rb
**/vendor/gems/mechanize-*/lib/mechanize/c*
**/vendor/gems/mechanize-*/lib/mechanize/d*
**/vendor/gems/mechanize-*/lib/mechanize/e*
**/vendor/gems/mechanize-*/lib/mechanize/f*
**/vendor/gems/mechanize-*/lib/mechanize/h*.rb
**/vendor/gems/mechanize-*/lib/mechanize/i*
**/vendor/gems/mechanize-*/lib/mechanize/p*
**/vendor/gems/mechanize-*/lib/mechanize/r*
**/vendor/gems/mechanize-*/lib/mechanize/t*
**/vendor/gems/mechanize-*/lib/mechanize/u*
**/vendor/gems/mechanize-*/lib/mechanize/x*
**/vendor/gems/mechanize-*/test/
# Ignore dependencies we don't wish to vendor
**/vendor/bundle/ruby/*/gems/ast-*/
@ -99,28 +108,20 @@
**/vendor/bundle/ruby/*/gems/coderay-*/
**/vendor/bundle/ruby/*/gems/colorize-*/
**/vendor/bundle/ruby/*/gems/commander-*/
**/vendor/bundle/ruby/*/gems/connection_pool-*/
**/vendor/bundle/ruby/*/gems/diff-lcs-*/
**/vendor/bundle/ruby/*/gems/docile-*/
**/vendor/bundle/ruby/*/gems/domain_name-*/
**/vendor/bundle/ruby/*/gems/ecma-re-validator-*/
**/vendor/bundle/ruby/*/gems/hana-*/
**/vendor/bundle/ruby/*/gems/highline-*/
**/vendor/bundle/ruby/*/gems/http-cookie-*/
**/vendor/bundle/ruby/*/gems/hpricot-*/
**/vendor/bundle/ruby/*/gems/jaro_winkler-*/
**/vendor/bundle/ruby/*/gems/json-*/
**/vendor/bundle/ruby/*/gems/json_schemer-*/
**/vendor/bundle/ruby/*/gems/method_source-*/
**/vendor/bundle/ruby/*/gems/mime-types-data-*/
**/vendor/bundle/ruby/*/gems/mime-types-*/
**/vendor/bundle/ruby/*/gems/mini_portile2-*/
**/vendor/bundle/ruby/*/gems/minitest-*/
**/vendor/bundle/ruby/*/gems/msgpack-*/
**/vendor/bundle/ruby/*/gems/mustache-*/
**/vendor/bundle/ruby/*/gems/net-http-digest_auth-*/
**/vendor/bundle/ruby/*/gems/net-http-persistent-*/
**/vendor/bundle/ruby/*/gems/nokogiri-*/
**/vendor/bundle/ruby/*/gems/ntlm-http-*/
**/vendor/bundle/ruby/*/gems/parallel-*/
**/vendor/bundle/ruby/*/gems/parallel_tests-*/
@ -129,7 +130,6 @@
**/vendor/bundle/ruby/*/gems/powerpack-*/
**/vendor/bundle/ruby/*/gems/psych-*/
**/vendor/bundle/ruby/*/gems/pry-*/
**/vendor/bundle/ruby/*/gems/racc-*/
**/vendor/bundle/ruby/*/gems/rainbow-*/
**/vendor/bundle/ruby/*/gems/rbi-*/
**/vendor/bundle/ruby/*/gems/rdiscount-*/
@ -158,14 +158,14 @@
**/vendor/bundle/ruby/*/gems/strscan-*/
**/vendor/bundle/ruby/*/gems/tapioca-*/
**/vendor/bundle/ruby/*/gems/thor-*/
**/vendor/bundle/ruby/*/gems/unf_ext-*/
**/vendor/bundle/ruby/*/gems/unf-*/
**/vendor/bundle/ruby/*/gems/unicode-display_width-*/
**/vendor/bundle/ruby/*/gems/unparser-*/
**/vendor/bundle/ruby/*/gems/uri_template-*/
**/vendor/bundle/ruby/*/gems/webrobots-*/
**/vendor/bundle/ruby/*/gems/yard-*/
**/vendor/bundle/ruby/*/gems/yard-sorbet-*/
**/vendor/cache/
**/vendor/specifications/
# Ignore `bin` contents (again).
/bin

View File

@ -49,7 +49,6 @@ end
gem "activesupport"
gem "addressable"
gem "concurrent-ruby"
gem "mechanize"
gem "patchelf"
gem "plist"
gem "rubocop-performance"

View File

@ -18,12 +18,9 @@ GEM
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.2.2)
connection_pool (2.4.0)
did_you_mean (1.6.3)
diff-lcs (1.5.0)
docile (1.4.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
ecma-re-validator (0.4.0)
regexp_parser (~> 2.2)
elftools (1.2.0)
@ -31,8 +28,6 @@ GEM
hana (1.3.7)
highline (2.0.3)
hpricot (0.8.6)
http-cookie (1.0.5)
domain_name (~> 0.5)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
json (2.6.3)
@ -41,39 +36,10 @@ GEM
hana (~> 1.3)
regexp_parser (~> 2.0)
uri_template (~> 0.7)
mechanize (2.9.1)
addressable (~> 2.8)
domain_name (~> 0.5, >= 0.5.20190701)
http-cookie (~> 1.0, >= 1.0.3)
mime-types (~> 3.0)
net-http-digest_auth (~> 1.4, >= 1.4.1)
net-http-persistent (>= 2.5.2, < 5.0.dev)
nokogiri (~> 1.11, >= 1.11.2)
rubyntlm (~> 0.6, >= 0.6.3)
webrick (~> 1.7)
webrobots (~> 0.1.2)
method_source (1.0.0)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
mini_portile2 (2.8.1)
minitest (5.18.0)
msgpack (1.7.0)
mustache (1.1.1)
net-http-digest_auth (1.4.1)
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
nokogiri (1.13.10-aarch64-linux)
racc (~> 1.4)
nokogiri (1.13.10-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.10-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.13.10-x86_64-linux)
racc (~> 1.4)
parallel (1.23.0)
parallel_tests (3.13.0)
parallel
@ -91,7 +57,6 @@ GEM
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.1)
racc (1.6.2)
rack (3.0.7)
rainbow (3.1.1)
rbi (0.0.14)
@ -159,7 +124,6 @@ GEM
ruby-macho (3.0.0)
ruby-prof (1.4.3)
ruby-progressbar (1.13.0)
rubyntlm (0.6.3)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
@ -202,17 +166,12 @@ GEM
thor (1.2.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.4.2)
unparser (0.6.4)
diff-lcs (~> 1.3)
parser (>= 3.1.0)
uri_template (0.7.0)
warning (1.3.0)
webrick (1.8.1)
webrobots (0.1.2)
yard (0.9.34)
yard-sorbet (0.6.1)
sorbet-runtime (>= 0.5)
@ -234,7 +193,6 @@ DEPENDENCIES
concurrent-ruby
did_you_mean
json_schemer
mechanize
minitest
parallel_tests
parlour

View File

@ -19,6 +19,9 @@ module Homebrew
description: "Update the specified list of vendored gems to the latest version."
switch "--no-commit",
description: "Do not generate a new commit upon completion."
switch "--non-bundler-gems",
description: "Update vendored gems that aren't using Bundler.",
hidden: true
named_args :none
end
@ -53,9 +56,24 @@ module Homebrew
ohai "bundle pristine"
safe_system "bundle", "pristine"
if args.non_bundler_gems?
%w[
mechanize
].each do |gem|
ohai "gem install #{gem}"
safe_system "gem", "install", "mechanize", "--install-dir", "vendor",
"--no-document", "--no-wrappers", "--ignore-dependencies", "--force"
(HOMEBREW_LIBRARY_PATH/"vendor/gems").cd do
if (source = Pathname.glob("#{gem}-*/").first)
FileUtils.ln_sf source, gem
end
end
end
end
unless args.no_commit?
ohai "git add vendor/bundle"
system "git", "add", "vendor/bundle"
ohai "git add vendor"
system "git", "add", "vendor"
Utils::Git.set_name_email!
Utils::Git.setup_gpg!

View File

@ -8,8 +8,12 @@ require "lazy_object"
require "cgi"
require "lock_file"
require "mechanize/version"
require "mechanize/http/content_disposition_parser"
# Need to define this before requiring Mechanize to avoid:
# uninitialized constant Mechanize
# rubocop:disable Lint/EmptyClass
class Mechanize; end
require "vendor/gems/mechanize/lib/mechanize/http/content_disposition_parser"
# rubocop:enable Lint/EmptyClass
require "utils/curl"
require "utils/github"

View File

@ -1,5 +1,7 @@
--dir=.
--enable-experimental-requires-ancestor
--ignore=/vendor
--ignore=/vendor/bundle
--ignore=/vendor/gems
--ignore=/vendor/portable-ruby
--ignore=/test/.gem
--suppress-error-code=7019

View File

@ -1,66 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `connection_pool` gem.
# Please instead update this file by running `bin/tapioca gem connection_pool`.
class ConnectionPool
def initialize(options = T.unsafe(nil), &block); end
def available; end
def checkin(force: T.unsafe(nil)); end
def checkout(options = T.unsafe(nil)); end
def reload(&block); end
def shutdown(&block); end
def size; end
def then(options = T.unsafe(nil)); end
def with(options = T.unsafe(nil)); end
class << self
def after_fork; end
def wrap(options, &block); end
end
end
ConnectionPool::DEFAULTS = T.let(T.unsafe(nil), Hash)
class ConnectionPool::Error < ::RuntimeError; end
ConnectionPool::INSTANCES = T.let(T.unsafe(nil), ObjectSpace::WeakMap[T.untyped])
class ConnectionPool::PoolShuttingDownError < ::ConnectionPool::Error; end
class ConnectionPool::TimedStack
def initialize(size = T.unsafe(nil), &block); end
def <<(obj, options = T.unsafe(nil)); end
def empty?; end
def length; end
def max; end
def pop(timeout = T.unsafe(nil), options = T.unsafe(nil)); end
def push(obj, options = T.unsafe(nil)); end
def shutdown(reload: T.unsafe(nil), &block); end
private
def connection_stored?(options = T.unsafe(nil)); end
def current_time; end
def fetch_connection(options = T.unsafe(nil)); end
def shutdown_connections(options = T.unsafe(nil)); end
def store_connection(obj, options = T.unsafe(nil)); end
def try_create(options = T.unsafe(nil)); end
end
class ConnectionPool::TimeoutError < ::Timeout::Error; end
ConnectionPool::VERSION = T.let(T.unsafe(nil), String)
class ConnectionPool::Wrapper < ::BasicObject
def initialize(options = T.unsafe(nil), &block); end
def method_missing(name, *args, &block); end
def pool_available; end
def pool_shutdown(&block); end
def pool_size; end
def respond_to?(id, *args); end
def with(&block); end
def wrapped_pool; end
end
ConnectionPool::Wrapper::METHODS = T.let(T.unsafe(nil), Array)

View File

@ -1,71 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `domain_name` gem.
# Please instead update this file by running `bin/tapioca gem domain_name`.
class DomainName
def initialize(hostname); end
def <(other); end
def <=(other); end
def <=>(other); end
def ==(other); end
def >(other); end
def >=(other); end
def canonical?; end
def canonical_tld?; end
def cookie_domain?(domain, host_only = T.unsafe(nil)); end
def domain; end
def domain_idn; end
def hostname; end
def hostname_idn; end
def idn; end
def inspect; end
def ipaddr; end
def ipaddr?; end
def superdomain; end
def tld; end
def tld_idn; end
def to_s; end
def to_str; end
def uri_host; end
class << self
def etld_data; end
def normalize(domain); end
end
end
DomainName::DOT = T.let(T.unsafe(nil), String)
DomainName::ETLD_DATA = T.let(T.unsafe(nil), Hash)
DomainName::ETLD_DATA_DATE = T.let(T.unsafe(nil), String)
module DomainName::Punycode
class << self
def decode(string); end
def decode_hostname(hostname); end
def encode(string); end
def encode_hostname(hostname); end
end
end
class DomainName::Punycode::ArgumentError < ::ArgumentError; end
DomainName::Punycode::BASE = T.let(T.unsafe(nil), Integer)
class DomainName::Punycode::BufferOverflowError < ::DomainName::Punycode::ArgumentError; end
DomainName::Punycode::CUTOFF = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::DAMP = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::DECODE_DIGIT = T.let(T.unsafe(nil), Hash)
DomainName::Punycode::DELIMITER = T.let(T.unsafe(nil), String)
DomainName::Punycode::DOT = T.let(T.unsafe(nil), String)
DomainName::Punycode::ENCODE_DIGIT = T.let(T.unsafe(nil), Proc)
DomainName::Punycode::INITIAL_BIAS = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::INITIAL_N = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::LOBASE = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::MAXINT = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::PREFIX = T.let(T.unsafe(nil), String)
DomainName::Punycode::RE_NONBASIC = T.let(T.unsafe(nil), Regexp)
DomainName::Punycode::SKEW = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::TMAX = T.let(T.unsafe(nil), Integer)
DomainName::Punycode::TMIN = T.let(T.unsafe(nil), Integer)
DomainName::VERSION = T.let(T.unsafe(nil), String)

View File

@ -1,216 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `http-cookie` gem.
# Please instead update this file by running `bin/tapioca gem http-cookie`.
module HTTP; end
class HTTP::Cookie
include ::Mechanize::CookieDeprecated
include ::Mechanize::CookieIMethods
include ::Comparable
extend ::Mechanize::CookieDeprecated
extend ::Mechanize::CookieCMethods
def initialize(*args); end
def <=>(other); end
def acceptable?; end
def acceptable_from_uri?(uri); end
def accessed_at; end
def accessed_at=(_arg0); end
def cookie_value; end
def created_at; end
def created_at=(_arg0); end
def domain; end
def domain=(domain); end
def domain_name; end
def dot_domain; end
def encode_with(coder); end
def expire!; end
def expired?(time = T.unsafe(nil)); end
def expires; end
def expires=(t); end
def expires_at; end
def expires_at=(t); end
def for_domain; end
def for_domain=(_arg0); end
def for_domain?; end
def httponly; end
def httponly=(_arg0); end
def httponly?; end
def init_with(coder); end
def inspect; end
def max_age; end
def max_age=(sec); end
def name; end
def name=(name); end
def origin; end
def origin=(origin); end
def path; end
def path=(path); end
def secure; end
def secure=(_arg0); end
def secure?; end
def session; end
def session?; end
def set_cookie_value; end
def to_s; end
def to_yaml_properties; end
def valid_for_uri?(uri); end
def value; end
def value=(value); end
def yaml_initialize(tag, map); end
class << self
def cookie_value(cookies); end
def cookie_value_to_hash(cookie_value); end
def path_match?(base_path, target_path); end
end
end
HTTP::Cookie::MAX_COOKIES_PER_DOMAIN = T.let(T.unsafe(nil), Integer)
HTTP::Cookie::MAX_COOKIES_TOTAL = T.let(T.unsafe(nil), Integer)
HTTP::Cookie::MAX_LENGTH = T.let(T.unsafe(nil), Integer)
HTTP::Cookie::PERSISTENT_PROPERTIES = T.let(T.unsafe(nil), Array)
class HTTP::Cookie::Scanner < ::StringScanner
def initialize(string, logger = T.unsafe(nil)); end
def parse_cookie_date(s); end
def scan_cookie; end
def scan_dquoted; end
def scan_name; end
def scan_name_value(comma_as_separator = T.unsafe(nil)); end
def scan_set_cookie; end
def scan_value(comma_as_separator = T.unsafe(nil)); end
def skip_wsp; end
private
def tuple_to_time(day_of_month, month, year, time); end
class << self
def quote(s); end
end
end
HTTP::Cookie::Scanner::RE_BAD_CHAR = T.let(T.unsafe(nil), Regexp)
HTTP::Cookie::Scanner::RE_COOKIE_COMMA = T.let(T.unsafe(nil), Regexp)
HTTP::Cookie::Scanner::RE_NAME = T.let(T.unsafe(nil), Regexp)
HTTP::Cookie::Scanner::RE_WSP = T.let(T.unsafe(nil), Regexp)
HTTP::Cookie::UNIX_EPOCH = T.let(T.unsafe(nil), Time)
HTTP::Cookie::VERSION = T.let(T.unsafe(nil), String)
class HTTP::CookieJar
include ::Mechanize::CookieDeprecated
include ::Mechanize::CookieJarIMethods
include ::Enumerable
def initialize(options = T.unsafe(nil)); end
def <<(cookie); end
def cleanup(session = T.unsafe(nil)); end
def clear; end
def cookies(url = T.unsafe(nil)); end
def delete(cookie); end
def each(uri = T.unsafe(nil), &block); end
def empty?(url = T.unsafe(nil)); end
def load(readable, *options); end
def parse(set_cookie, origin, options = T.unsafe(nil)); end
def save(writable, *options); end
def store; end
private
def get_impl(base, value, *args); end
def initialize_copy(other); end
class << self
def const_missing(name); end
end
end
class HTTP::CookieJar::AbstractSaver
def initialize(options = T.unsafe(nil)); end
def load(io, jar); end
def save(io, jar); end
private
def default_options; end
class << self
def class_to_symbol(klass); end
def implementation(symbol); end
def inherited(subclass); end
end
end
class HTTP::CookieJar::AbstractStore
include ::MonitorMixin
include ::Enumerable
def initialize(options = T.unsafe(nil)); end
def add(cookie); end
def cleanup(session = T.unsafe(nil)); end
def clear; end
def delete(cookie); end
def each(uri = T.unsafe(nil), &block); end
def empty?; end
private
def default_options; end
def initialize_copy(other); end
class << self
def class_to_symbol(klass); end
def implementation(symbol); end
def inherited(subclass); end
end
end
class HTTP::CookieJar::CookiestxtSaver < ::HTTP::CookieJar::AbstractSaver
def load(io, jar); end
def save(io, jar); end
private
def cookie_to_record(cookie); end
def default_options; end
def parse_record(line); end
end
HTTP::CookieJar::CookiestxtSaver::False = T.let(T.unsafe(nil), String)
HTTP::CookieJar::CookiestxtSaver::HTTPONLY_PREFIX = T.let(T.unsafe(nil), String)
HTTP::CookieJar::CookiestxtSaver::RE_HTTPONLY_PREFIX = T.let(T.unsafe(nil), Regexp)
HTTP::CookieJar::CookiestxtSaver::True = T.let(T.unsafe(nil), String)
class HTTP::CookieJar::HashStore < ::HTTP::CookieJar::AbstractStore
def initialize(options = T.unsafe(nil)); end
def add(cookie); end
def cleanup(session = T.unsafe(nil)); end
def clear; end
def default_options; end
def delete(cookie); end
def each(uri = T.unsafe(nil)); end
private
def initialize_copy(other); end
end
class HTTP::CookieJar::YAMLSaver < ::HTTP::CookieJar::AbstractSaver
def load(io, jar); end
def save(io, jar); end
private
def default_options; end
def load_yaml(yaml); end
end

View File

@ -1,73 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `mime-types-data` gem.
# Please instead update this file by running `bin/tapioca gem mime-types-data`.
module MIME; end
class MIME::Types
include ::Enumerable
extend ::Enumerable
def initialize; end
def [](type_id, complete: T.unsafe(nil), registered: T.unsafe(nil)); end
def add(*types); end
def add_type(type, quiet = T.unsafe(nil)); end
def count; end
def each; end
def inspect; end
def of(filename); end
def type_for(filename); end
private
def add_type_variant!(mime_type); end
def index_extensions!(mime_type); end
def match(pattern); end
def prune_matches(matches, complete, registered); end
def reindex_extensions!(mime_type); end
class << self
def [](type_id, complete: T.unsafe(nil), registered: T.unsafe(nil)); end
def add(*types); end
def count; end
def each; end
def logger; end
def logger=(_arg0); end
def new(*_arg0); end
def of(filename); end
def type_for(filename); end
private
def __instances__; end
def __types__; end
def lazy_load?; end
def load_default_mime_types(mode = T.unsafe(nil)); end
def load_mode; end
def reindex_extensions(type); end
end
end
class MIME::Types::Cache < ::Struct
def data; end
def data=(_); end
def version; end
def version=(_); end
class << self
def [](*_arg0); end
def inspect; end
def load(cache_file = T.unsafe(nil)); end
def members; end
def new(*_arg0); end
def save(types = T.unsafe(nil), cache_file = T.unsafe(nil)); end
end
end
module MIME::Types::Data; end
MIME::Types::Data::PATH = T.let(T.unsafe(nil), String)
MIME::Types::Data::VERSION = T.let(T.unsafe(nil), String)
MIME::Types::VERSION = T.let(T.unsafe(nil), String)

View File

@ -1,8 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `net-http-digest_auth` gem.
# Please instead update this file by running `bin/tapioca gem net-http-digest_auth`.
# THIS IS AN EMPTY RBI FILE.
# see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires

View File

@ -1,146 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `net-http-persistent` gem.
# Please instead update this file by running `bin/tapioca gem net-http-persistent`.
class Net::HTTP::Persistent
def initialize(name: T.unsafe(nil), proxy: T.unsafe(nil), pool_size: T.unsafe(nil)); end
def ca_file; end
def ca_file=(file); end
def ca_path; end
def ca_path=(path); end
def cert; end
def cert=(certificate); end
def cert_store; end
def cert_store=(store); end
def certificate; end
def certificate=(certificate); end
def ciphers; end
def ciphers=(ciphers); end
def connection_for(uri); end
def debug_output; end
def debug_output=(_arg0); end
def escape(str); end
def expired?(connection); end
def finish(connection); end
def generation; end
def headers; end
def http_version(uri); end
def http_versions; end
def idle_timeout; end
def idle_timeout=(_arg0); end
def keep_alive; end
def keep_alive=(_arg0); end
def key; end
def key=(key); end
def max_requests; end
def max_requests=(_arg0); end
def max_retries; end
def max_retries=(retries); end
def max_version; end
def max_version=(max_version); end
def min_version; end
def min_version=(min_version); end
def name; end
def no_proxy; end
def normalize_uri(uri); end
def open_timeout; end
def open_timeout=(_arg0); end
def override_headers; end
def pipeline(uri, requests, &block); end
def pool; end
def private_key; end
def private_key=(key); end
def proxy=(proxy); end
def proxy_bypass?(host, port); end
def proxy_from_env; end
def proxy_uri; end
def read_timeout; end
def read_timeout=(_arg0); end
def reconnect; end
def reconnect_ssl; end
def request(uri, req = T.unsafe(nil), &block); end
def request_setup(req_or_uri); end
def reset(connection); end
def reuse_ssl_sessions; end
def reuse_ssl_sessions=(_arg0); end
def shutdown; end
def socket_options; end
def ssl(connection); end
def ssl_generation; end
def ssl_timeout; end
def ssl_timeout=(ssl_timeout); end
def ssl_version; end
def ssl_version=(ssl_version); end
def start(http); end
def timeout_key; end
def unescape(str); end
def verify_callback; end
def verify_callback=(callback); end
def verify_depth; end
def verify_depth=(verify_depth); end
def verify_mode; end
def verify_mode=(verify_mode); end
def write_timeout; end
def write_timeout=(_arg0); end
class << self
def detect_idle_timeout(uri, max = T.unsafe(nil)); end
end
end
class Net::HTTP::Persistent::Connection
def initialize(http_class, http_args, ssl_generation); end
def close; end
def finish; end
def http; end
def http=(_arg0); end
def last_use; end
def last_use=(_arg0); end
def requests; end
def requests=(_arg0); end
def reset; end
def ressl(ssl_generation); end
def ssl_generation; end
def ssl_generation=(_arg0); end
end
Net::HTTP::Persistent::DEFAULT_POOL_SIZE = T.let(T.unsafe(nil), Integer)
Net::HTTP::Persistent::EPOCH = T.let(T.unsafe(nil), Time)
class Net::HTTP::Persistent::Error < ::StandardError; end
Net::HTTP::Persistent::HAVE_OPENSSL = T.let(T.unsafe(nil), String)
class Net::HTTP::Persistent::Pool < ::ConnectionPool
def initialize(options = T.unsafe(nil), &block); end
def available; end
def checkin(net_http_args); end
def checkout(net_http_args); end
def key; end
def shutdown; end
end
class Net::HTTP::Persistent::TimedStackMulti < ::ConnectionPool::TimedStack
def initialize(size = T.unsafe(nil), &block); end
def empty?; end
def length; end
private
def connection_stored?(options = T.unsafe(nil)); end
def fetch_connection(options = T.unsafe(nil)); end
def lru_update(connection_args); end
def shutdown_connections; end
def store_connection(obj, options = T.unsafe(nil)); end
def try_create(options = T.unsafe(nil)); end
class << self
def hash_of_arrays; end
end
end
Net::HTTP::Persistent::VERSION = T.let(T.unsafe(nil), String)

File diff suppressed because it is too large Load Diff

View File

@ -1,642 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `racc` gem.
# Please instead update this file by running `bin/tapioca gem racc`.
::APPLE_GEM_HOME = T.let(T.unsafe(nil), String)
::RUBY19 = T.let(T.unsafe(nil), TrueClass)
::RUBY_FRAMEWORK = T.let(T.unsafe(nil), TrueClass)
::RUBY_FRAMEWORK_VERSION = T.let(T.unsafe(nil), String)
class Object < ::BasicObject
include ::ActiveSupport::ToJsonWithActiveSupportEncoder
include ::ActiveSupport::ForkTracker::CoreExt
include ::ActiveSupport::ForkTracker::CoreExtPrivate
include ::Kernel
include ::JSON::Ext::Generator::GeneratorMethods::Object
include ::PP::ObjectMixin
include ::ActiveSupport::Dependencies::Loadable
include ::ActiveSupport::Tryable
end
ParseError = Racc::ParseError
class Racc::Accept
def inspect; end
end
class Racc::ActionTable
def initialize(rt, st); end
def accept; end
def each_reduce(&block); end
def each_shift(&block); end
def error; end
def init; end
def reduce(i); end
def reduce_n; end
def shift(i); end
def shift_n; end
end
class Racc::CompileError < ::Racc::Error; end
Racc::Copyright = T.let(T.unsafe(nil), String)
class Racc::DebugFlags
def initialize(parse = T.unsafe(nil), rule = T.unsafe(nil), token = T.unsafe(nil), state = T.unsafe(nil), la = T.unsafe(nil), prec = T.unsafe(nil), conf = T.unsafe(nil)); end
def any?; end
def la; end
def parse; end
def prec; end
def rule; end
def state; end
def status_logging; end
def token; end
class << self
def parse_option_string(s); end
end
end
class Racc::Error < ::StandardError
def inspect; end
end
class Racc::Goto
def initialize(ident, sym, from, to); end
def from_state; end
def ident; end
def inspect; end
def symbol; end
def to_state; end
end
class Racc::Grammar
extend ::Forwardable
def initialize(debug_flags = T.unsafe(nil)); end
def [](x); end
def add(rule); end
def added?(sym); end
def declare_precedence(assoc, syms); end
def dfa; end
def each(&block); end
def each_index(&block); end
def each_nonterminal(*args, &block); end
def each_rule(&block); end
def each_symbol(*args, &block); end
def each_terminal(*args, &block); end
def each_useless_nonterminal; end
def each_useless_rule; end
def each_with_index(&block); end
def end_precedence_declaration(reverse); end
def init; end
def intern(value, dummy = T.unsafe(nil)); end
def n_expected_srconflicts; end
def n_expected_srconflicts=(_arg0); end
def n_useless_nonterminals; end
def n_useless_rules; end
def nfa; end
def nonterminal_base; end
def parser_class; end
def size; end
def start; end
def start_symbol=(s); end
def state_transition_table; end
def states; end
def symbols; end
def symboltable; end
def to_s; end
def useless_nonterminal_exist?; end
def useless_rule_exist?; end
def write_log(path); end
private
def _compute_expand(t, set, lock); end
def add_start_rule; end
def check_rules_nullable(rules); end
def check_rules_useless(rules); end
def check_symbols_nullable(symbols); end
def check_symbols_useless(s); end
def compute_expand(t); end
def compute_hash; end
def compute_heads; end
def compute_locate; end
def compute_nullable; end
def compute_nullable_0; end
def compute_useless; end
def determine_terminals; end
def fix_ident; end
class << self
def define(&block); end
end
end
class Racc::Grammar::DefinitionEnv
def initialize; end
def _(&block); end
def _add(target, x); end
def _added?(sym); end
def _delayed_add(rule); end
def _intern(x); end
def action(&block); end
def flush_delayed; end
def grammar; end
def many(sym, &block); end
def many1(sym, &block); end
def method_missing(mid, *args, &block); end
def null(&block); end
def option(sym, default = T.unsafe(nil), &block); end
def precedence_table(&block); end
def separated_by(sep, sym, &block); end
def separated_by1(sep, sym, &block); end
def seq(*list, &block); end
private
def _defmetasyntax(type, id, action, &block); end
def _register(target_name); end
def _wrap(target_name, sym, block); end
end
class Racc::Grammar::PrecedenceDefinitionEnv
def initialize(g); end
def higher; end
def left(*syms); end
def lower; end
def nonassoc(*syms); end
def reverse; end
def right(*syms); end
end
class Racc::ISet
def initialize(a = T.unsafe(nil)); end
def [](key); end
def []=(key, val); end
def add(i); end
def clear; end
def delete(key); end
def dup; end
def each(&block); end
def empty?; end
def include?(key); end
def inspect; end
def key?(key); end
def set; end
def size; end
def to_a; end
def to_s; end
def update(other); end
def update_a(a); end
end
class Racc::Item
def initialize(rule, la); end
def each_la(tbl); end
def la; end
def rule; end
end
class Racc::LocationPointer
def initialize(rule, i, sym); end
def ==(ot); end
def before(len); end
def dereference; end
def eql?(ot); end
def hash; end
def head?; end
def ident; end
def increment; end
def index; end
def inspect; end
def next; end
def reduce; end
def reduce?; end
def rule; end
def symbol; end
def to_s; end
private
def ptr_bug!; end
end
class Racc::LogFileGenerator
def initialize(states, debug_flags = T.unsafe(nil)); end
def action_out(f, state); end
def outact(f, t, act); end
def output(out); end
def output_conflict(out); end
def output_rule(out); end
def output_state(out); end
def output_token(out); end
def output_useless(out); end
def outrrconf(f, confs); end
def outsrconf(f, confs); end
def pointer_out(out, ptr); end
def symbol_locations(locs); end
end
class Racc::OrMark
def initialize(lineno); end
def inspect; end
def lineno; end
def name; end
end
class Racc::Parser
def _racc_do_parse_rb(arg, in_debug); end
def _racc_do_reduce(arg, act); end
def _racc_evalact(act, arg); end
def _racc_init_sysvars; end
def _racc_setup; end
def _racc_yyparse_rb(recv, mid, arg, c_debug); end
def next_token; end
def on_error(t, val, vstack); end
def racc_accept; end
def racc_e_pop(state, tstack, vstack); end
def racc_next_state(curstate, state); end
def racc_print_stacks(t, v); end
def racc_print_states(s); end
def racc_read_token(t, tok, val); end
def racc_reduce(toks, sim, tstack, vstack); end
def racc_shift(tok, tstack, vstack); end
def racc_token2str(tok); end
def token_to_str(t); end
def yyaccept; end
def yyerrok; end
def yyerror; end
class << self
def racc_runtime_type; end
end
end
Racc::Parser::Racc_Main_Parsing_Routine = T.let(T.unsafe(nil), Symbol)
Racc::Parser::Racc_Runtime_Core_Id_C = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_Runtime_Core_Version = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_Runtime_Core_Version_C = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_Runtime_Core_Version_R = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_Runtime_Type = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_Runtime_Version = T.let(T.unsafe(nil), String)
Racc::Parser::Racc_YY_Parse_Method = T.let(T.unsafe(nil), Symbol)
class Racc::ParserClassGenerator
def initialize(states); end
def generate; end
private
def define_actions(c); end
end
class Racc::Prec
def initialize(symbol, lineno); end
def inspect; end
def lineno; end
def name; end
def symbol; end
end
class Racc::RRconflict
def initialize(sid, high, low, tok); end
def high_prec; end
def low_prec; end
def stateid; end
def to_s; end
def token; end
end
class Racc::Reduce
def initialize(rule); end
def decref; end
def incref; end
def inspect; end
def refn; end
def rule; end
def ruleid; end
end
class Racc::Rule
def initialize(target, syms, act); end
def ==(other); end
def [](idx); end
def accept?; end
def action; end
def each(&block); end
def each_rule(&block); end
def empty?; end
def hash; end
def hash=(n); end
def ident; end
def ident=(_arg0); end
def inspect; end
def null=(n); end
def nullable?; end
def prec(sym, &block); end
def precedence; end
def precedence=(sym); end
def ptrs; end
def replace(src, dest); end
def rule; end
def size; end
def specified_prec; end
def specified_prec=(_arg0); end
def symbols; end
def target; end
def target=(_arg0); end
def to_s; end
def useless=(u); end
def useless?; end
def |(x); end
end
class Racc::SRconflict
def initialize(sid, shift, reduce); end
def reduce; end
def shift; end
def stateid; end
def to_s; end
end
class Racc::Shift
def initialize(goto); end
def goto_id; end
def goto_state; end
def inspect; end
end
class Racc::SourceText
def initialize(text, filename, lineno); end
def filename; end
def lineno; end
def location; end
def text; end
def to_s; end
end
class Racc::State
def initialize(ident, core); end
def ==(oth); end
def action; end
def check_la(la_rules); end
def closure; end
def conflict?; end
def core; end
def defact; end
def defact=(_arg0); end
def eql?(oth); end
def goto_table; end
def gotos; end
def hash; end
def ident; end
def inspect; end
def la=(la); end
def make_closure(core); end
def n_rrconflicts; end
def n_srconflicts; end
def ritems; end
def rr_conflict(high, low, ctok); end
def rrconf; end
def rruleid(rule); end
def rrules; end
def sr_conflict(shift, reduce); end
def srconf; end
def stateid; end
def stokens; end
def to_s; end
end
class Racc::StateTransitionTable < ::Struct
def initialize(states); end
def action_check; end
def action_check=(_); end
def action_default; end
def action_default=(_); end
def action_pointer; end
def action_pointer=(_); end
def action_table; end
def action_table=(_); end
def debug_parser; end
def debug_parser=(_); end
def goto_check; end
def goto_check=(_); end
def goto_default; end
def goto_default=(_); end
def goto_pointer; end
def goto_pointer=(_); end
def goto_table; end
def goto_table=(_); end
def grammar; end
def nt_base; end
def nt_base=(_); end
def parser_class; end
def reduce_n; end
def reduce_n=(_); end
def reduce_table; end
def reduce_table=(_); end
def shift_n; end
def shift_n=(_); end
def states; end
def token_table; end
def token_table=(_); end
def token_to_s_table; end
def token_to_s_table=(_); end
def token_value_table; end
def use_result_var; end
def use_result_var=(_); end
class << self
def [](*_arg0); end
def generate(states); end
def inspect; end
def members; end
def new(*_arg0); end
end
end
class Racc::StateTransitionTableGenerator
def initialize(states); end
def act2actid(act); end
def addent(all, arr, chkval, ptr); end
def gen_action_tables(t, states); end
def gen_goto_tables(t, grammar); end
def generate; end
def mkmapexp(arr); end
def reduce_table(grammar); end
def set_table(entries, dummy, tbl, chk, ptr); end
def token_table(grammar); end
end
Racc::StateTransitionTableGenerator::RE_DUP_MAX = T.let(T.unsafe(nil), Integer)
class Racc::States
include ::Enumerable
extend ::Forwardable
def initialize(grammar, debug_flags = T.unsafe(nil)); end
def [](i); end
def actions; end
def dfa; end
def each(&block); end
def each_index(&block); end
def each_state(&block); end
def grammar; end
def inspect; end
def n_rrconflicts; end
def n_srconflicts; end
def nfa; end
def nt_base(*args, &block); end
def reduce_n(*args, &block); end
def rrconflict_exist?; end
def shift_n(*args, &block); end
def should_report_srconflict?; end
def size; end
def srconflict_exist?; end
def state_transition_table; end
def to_s; end
private
def addrel(tbl, i, item); end
def addsym(table, sym, ptr); end
def check_useless; end
def compute_dfa; end
def compute_nfa; end
def core_to_state(core); end
def create_tmap(size); end
def digraph(map, relation); end
def do_resolve_sr(stok, rtok); end
def each_t(tbl, set); end
def fingerprint(arr); end
def generate_states(state); end
def lookahead; end
def pack(state); end
def print_atab(idx, tab); end
def print_tab(idx, rel, tab); end
def print_tab_i(idx, rel, tab, i); end
def printb(i); end
def record_path(begst, rule); end
def resolve(state); end
def resolve_rr(state, r); end
def resolve_sr(state, s); end
def set_accept; end
def transpose(rel); end
def traverse(i, index, vertices, map, relation); end
end
Racc::States::ASSOC = T.let(T.unsafe(nil), Hash)
class Racc::Sym
def initialize(value, dummyp); end
def assoc; end
def assoc=(_arg0); end
def dummy?; end
def expand; end
def expand=(v); end
def hash; end
def heads; end
def ident; end
def ident=(v); end
def inspect; end
def locate; end
def nonterminal?; end
def null=(n); end
def nullable?; end
def precedence; end
def precedence=(_arg0); end
def rule; end
def self_null?; end
def serialize; end
def serialized=(_arg0); end
def should_terminal; end
def should_terminal?; end
def snull=(v); end
def string_symbol?; end
def term=(t); end
def terminal?; end
def to_s; end
def useless=(f); end
def useless?; end
def value; end
def |(x); end
class << self
def once_writer(nm); end
end
end
class Racc::SymbolTable
include ::Enumerable
def initialize; end
def [](id); end
def anchor; end
def delete(sym); end
def dummy; end
def each(&block); end
def each_nonterminal(&block); end
def each_terminal(&block); end
def error; end
def fix; end
def intern(val, dummy = T.unsafe(nil)); end
def nonterminals; end
def nt_base; end
def nt_max; end
def symbols; end
def terminals(&block); end
def to_a; end
private
def check_terminals; end
def fix_ident; end
end
class Racc::UserAction
def initialize(src, proc); end
def empty?; end
def inspect; end
def name; end
def proc; end
def proc?; end
def source; end
def source?; end
class << self
def empty; end
def proc(pr = T.unsafe(nil), &block); end
def source_text(src); end
end
end
Racc::VERSION = T.let(T.unsafe(nil), String)
Racc::Version = T.let(T.unsafe(nil), String)

View File

@ -1,332 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `rubyntlm` gem.
# Please instead update this file by running `bin/tapioca gem rubyntlm`.
module Net::NTLM
class << self
def apply_des(plain, keys); end
def gen_keys(str); end
def is_ntlm_hash?(data); end
def lm_hash(password); end
def lm_response(arg); end
def lmv2_response(arg, opt = T.unsafe(nil)); end
def ntlm2_session(arg, opt = T.unsafe(nil)); end
def ntlm_hash(password, opt = T.unsafe(nil)); end
def ntlm_response(arg); end
def ntlmv2_hash(user, password, target, opt = T.unsafe(nil)); end
def ntlmv2_response(arg, opt = T.unsafe(nil)); end
def pack_int64le(val); end
def split7(str); end
end
end
class Net::NTLM::Blob < ::Net::NTLM::FieldSet
def blob_signature; end
def blob_signature=(val); end
def challenge; end
def challenge=(val); end
def parse(str, offset = T.unsafe(nil)); end
def reserved; end
def reserved=(val); end
def target_info; end
def target_info=(val); end
def timestamp; end
def timestamp=(val); end
def unknown1; end
def unknown1=(val); end
def unknown2; end
def unknown2=(val); end
end
class Net::NTLM::ChannelBinding
def initialize(outer_channel); end
def acceptor_address_length; end
def acceptor_addrtype; end
def application_data; end
def channel; end
def channel_binding_token; end
def channel_hash; end
def gss_channel_bindings_struct; end
def initiator_address_length; end
def initiator_addtype; end
def unique_prefix; end
class << self
def create(outer_channel); end
end
end
class Net::NTLM::Client
def initialize(username, password, opts = T.unsafe(nil)); end
def domain; end
def flags; end
def init_context(resp = T.unsafe(nil), channel_binding = T.unsafe(nil)); end
def password; end
def session; end
def session_key; end
def username; end
def workstation; end
private
def type1_message; end
end
class Net::NTLM::Client::Session
def initialize(client, challenge_message, channel_binding = T.unsafe(nil)); end
def authenticate!; end
def challenge_message; end
def channel_binding; end
def client; end
def exported_session_key; end
def seal_message(message); end
def sign_message(message); end
def unseal_message(emessage); end
def verify_signature(signature, message); end
private
def blob; end
def calculate_user_session_key!; end
def client_challenge; end
def client_cipher; end
def client_seal_key; end
def client_sign_key; end
def domain; end
def lmv2_resp; end
def negotiate_key_exchange?; end
def nt_proof_str; end
def ntlmv2_hash; end
def ntlmv2_resp; end
def oem_or_unicode_str(str); end
def password; end
def raw_sequence; end
def sequence; end
def server_challenge; end
def server_cipher; end
def server_seal_key; end
def server_sign_key; end
def target_info; end
def timestamp; end
def use_oem_strings?; end
def user_session_key; end
def username; end
def workstation; end
end
class Net::NTLM::EncodeUtil
class << self
def decode_utf16le(str); end
def encode_utf16le(str); end
end
end
class Net::NTLM::Field
def initialize(opts); end
def active; end
def active=(_arg0); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def size; end
def value; end
def value=(_arg0); end
end
class Net::NTLM::FieldSet
def initialize; end
def [](name); end
def []=(name, val); end
def disable(name); end
def enable(name); end
def has_disabled_fields?; end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def size; end
class << self
def int16LE(name, opts); end
def int32LE(name, opts); end
def int64LE(name, opts); end
def names; end
def opts; end
def prototypes; end
def security_buffer(name, opts); end
def string(name, opts); end
def types; end
private
def add_field(name, type, opts); end
def define_accessor(name); end
end
end
class Net::NTLM::Int16LE < ::Net::NTLM::Field
def initialize(opt); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
end
class Net::NTLM::Int32LE < ::Net::NTLM::Field
def initialize(opt); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
end
class Net::NTLM::Int64LE < ::Net::NTLM::Field
def initialize(opt); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
end
class Net::NTLM::InvalidTargetDataError < ::Net::NTLM::NtlmError
def initialize(msg, data); end
def data; end
end
class Net::NTLM::Message < ::Net::NTLM::FieldSet
def data_edge; end
def data_size; end
def decode64(str); end
def deflag; end
def dump_flags; end
def encode64; end
def has_flag?(flag); end
def head_size; end
def parse(str); end
def security_buffers; end
def serialize; end
def set_flag(flag); end
def size; end
class << self
def decode64(str); end
def parse(str); end
end
end
class Net::NTLM::Message::Type0 < ::Net::NTLM::Message
def sign; end
def sign=(val); end
def type; end
def type=(val); end
end
class Net::NTLM::Message::Type1 < ::Net::NTLM::Message
def domain; end
def domain=(val); end
def flag; end
def flag=(val); end
def os_version; end
def os_version=(val); end
def sign; end
def sign=(val); end
def type; end
def type=(val); end
def workstation; end
def workstation=(val); end
end
class Net::NTLM::Message::Type2 < ::Net::NTLM::Message
def challenge; end
def challenge=(val); end
def context; end
def context=(val); end
def flag; end
def flag=(val); end
def os_version; end
def os_version=(val); end
def response(arg, opt = T.unsafe(nil)); end
def sign; end
def sign=(val); end
def target_info; end
def target_info=(val); end
def target_name; end
def target_name=(val); end
def type; end
def type=(val); end
end
class Net::NTLM::Message::Type3 < ::Net::NTLM::Message
def blank_password?(server_challenge); end
def domain; end
def domain=(val); end
def flag; end
def flag=(val); end
def lm_response; end
def lm_response=(val); end
def ntlm_response; end
def ntlm_response=(val); end
def ntlm_version; end
def os_version; end
def os_version=(val); end
def password?(password, server_challenge); end
def session_key; end
def session_key=(val); end
def sign; end
def sign=(val); end
def type; end
def type=(val); end
def user; end
def user=(val); end
def workstation; end
def workstation=(val); end
private
def ntlm2_session_password?(password, server_challenge); end
def ntlmv2_password?(password, server_challenge); end
class << self
def create(arg, opt = T.unsafe(nil)); end
end
end
class Net::NTLM::SecurityBuffer < ::Net::NTLM::FieldSet
def initialize(opts = T.unsafe(nil)); end
def active; end
def active=(_arg0); end
def allocated; end
def allocated=(val); end
def data_size; end
def length; end
def length=(val); end
def offset; end
def offset=(val); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def value; end
def value=(val); end
end
class Net::NTLM::String < ::Net::NTLM::Field
def initialize(opts); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def value=(val); end
end
class Net::NTLM::TargetInfo
def initialize(av_pair_sequence); end
def av_pairs; end
def to_s; end
private
def read_pairs(av_pair_sequence); end
def to_hex(str); end
end

View File

@ -1,23 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `unf` gem.
# Please instead update this file by running `bin/tapioca gem unf`.
module UNF; end
class UNF::Normalizer
include ::Singleton
extend ::Singleton::SingletonClassMethods
def initialize; end
def normalize(_arg0, _arg1); end
class << self
def instance; end
def normalize(string, form); end
end
end
UNF::VERSION = T.let(T.unsafe(nil), String)

View File

@ -1,8 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `unf_ext` gem.
# Please instead update this file by running `bin/tapioca gem unf_ext`.
# THIS IS AN EMPTY RBI FILE.
# see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires

View File

@ -1,614 +0,0 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `webrick` gem.
# Please instead update this file by running `bin/tapioca gem webrick`.
module WEBrick::AccessLog
private
def escape(data); end
def format(format_string, params); end
def setup_params(config, req, res); end
class << self
def escape(data); end
def format(format_string, params); end
def setup_params(config, req, res); end
end
end
class WEBrick::BasicLog
def initialize(log_file = T.unsafe(nil), level = T.unsafe(nil)); end
def <<(obj); end
def close; end
def debug(msg); end
def debug?; end
def error(msg); end
def error?; end
def fatal(msg); end
def fatal?; end
def info(msg); end
def info?; end
def level; end
def level=(_arg0); end
def log(level, data); end
def warn(msg); end
def warn?; end
private
def format(arg); end
end
class WEBrick::GenericServer
def initialize(config = T.unsafe(nil), default = T.unsafe(nil)); end
def [](key); end
def config; end
def listen(address, port); end
def listeners; end
def logger; end
def run(sock); end
def shutdown; end
def start(&block); end
def status; end
def stop; end
def tokens; end
private
def accept_client(svr); end
def alarm_shutdown_pipe; end
def call_callback(callback_name, *args); end
def cleanup_listener; end
def cleanup_shutdown_pipe(shutdown_pipe); end
def setup_shutdown_pipe; end
def start_thread(sock, &block); end
end
module WEBrick::HTMLUtils
private
def escape(string); end
class << self
def escape(string); end
end
end
module WEBrick::HTTPAuth
private
def _basic_auth(req, res, realm, req_field, res_field, err_type, block); end
def basic_auth(req, res, realm, &block); end
def proxy_basic_auth(req, res, realm, &block); end
class << self
def _basic_auth(req, res, realm, req_field, res_field, err_type, block); end
def basic_auth(req, res, realm, &block); end
def proxy_basic_auth(req, res, realm, &block); end
end
end
module WEBrick::HTTPAuth::Authenticator
def logger; end
def realm; end
def userdb; end
private
def check_init(config); end
def check_scheme(req); end
def error(fmt, *args); end
def info(fmt, *args); end
def log(meth, fmt, *args); end
end
WEBrick::HTTPAuth::Authenticator::AuthException = WEBrick::HTTPStatus::Unauthorized
class WEBrick::HTTPAuth::BasicAuth
include ::WEBrick::HTTPAuth::Authenticator
def initialize(config, default = T.unsafe(nil)); end
def authenticate(req, res); end
def challenge(req, res); end
def logger; end
def realm; end
def userdb; end
class << self
def make_passwd(realm, user, pass); end
end
end
class WEBrick::HTTPAuth::DigestAuth
include ::WEBrick::HTTPAuth::Authenticator
def initialize(config, default = T.unsafe(nil)); end
def algorithm; end
def authenticate(req, res); end
def challenge(req, res, stale = T.unsafe(nil)); end
def qop; end
private
def _authenticate(req, res); end
def check_nonce(req, auth_req); end
def check_opaque(opaque_struct, req, auth_req); end
def check_uri(req, auth_req); end
def generate_next_nonce(req); end
def generate_opaque(req); end
def hexdigest(*args); end
def split_param_value(string); end
class << self
def make_passwd(realm, user, pass); end
end
end
class WEBrick::HTTPAuth::DigestAuth::OpaqueInfo < ::Struct
def nc=(_); end
def nonce=(_); end
def time=(_); end
end
class WEBrick::HTTPAuth::Htdigest
include ::WEBrick::HTTPAuth::UserDB
def initialize(path); end
def delete_passwd(realm, user); end
def each; end
def flush(output = T.unsafe(nil)); end
def get_passwd(realm, user, reload_db); end
def reload; end
def set_passwd(realm, user, pass); end
end
class WEBrick::HTTPAuth::Htgroup
def initialize(path); end
def add(group, members); end
def flush(output = T.unsafe(nil)); end
def members(group); end
def reload; end
end
class WEBrick::HTTPAuth::Htpasswd
include ::WEBrick::HTTPAuth::UserDB
def initialize(path, password_hash: T.unsafe(nil)); end
def delete_passwd(realm, user); end
def each; end
def flush(output = T.unsafe(nil)); end
def get_passwd(realm, user, reload_db); end
def reload; end
def set_passwd(realm, user, pass); end
end
WEBrick::HTTPAuth::ProxyAuthenticator::AuthException = WEBrick::HTTPStatus::ProxyAuthenticationRequired
class WEBrick::HTTPAuth::ProxyBasicAuth < ::WEBrick::HTTPAuth::BasicAuth
include ::WEBrick::HTTPAuth::ProxyAuthenticator
end
class WEBrick::HTTPAuth::ProxyDigestAuth < ::WEBrick::HTTPAuth::DigestAuth
include ::WEBrick::HTTPAuth::ProxyAuthenticator
private
def check_uri(req, auth_req); end
end
module WEBrick::HTTPAuth::UserDB
def auth_type; end
def auth_type=(_arg0); end
def get_passwd(realm, user, reload_db = T.unsafe(nil)); end
def make_passwd(realm, user, pass); end
def set_passwd(realm, user, pass); end
end
class WEBrick::HTTPRequest
def initialize(config); end
def [](header_name); end
def accept; end
def accept_charset; end
def accept_encoding; end
def accept_language; end
def addr; end
def attributes; end
def body(&block); end
def body_reader; end
def content_length; end
def content_type; end
def continue; end
def cookies; end
def each; end
def fixup; end
def header; end
def host; end
def http_version; end
def keep_alive; end
def keep_alive?; end
def meta_vars; end
def parse(socket = T.unsafe(nil)); end
def path; end
def path_info; end
def path_info=(_arg0); end
def peeraddr; end
def port; end
def query; end
def query_string; end
def query_string=(_arg0); end
def raw_header; end
def readpartial(size, buf = T.unsafe(nil)); end
def remote_ip; end
def request_line; end
def request_method; end
def request_time; end
def request_uri; end
def script_name; end
def script_name=(_arg0); end
def server_name; end
def ssl?; end
def to_s; end
def unparsed_uri; end
def user; end
def user=(_arg0); end
private
def _read_data(io, method, *arg); end
def parse_host_request_line(host); end
def parse_query; end
def parse_uri(str, scheme = T.unsafe(nil)); end
def read_body(socket, block); end
def read_chunk_size(socket); end
def read_chunked(socket, block); end
def read_data(io, size); end
def read_header(socket); end
def read_line(io, size = T.unsafe(nil)); end
def read_request_line(socket); end
def setup_forwarded_info; end
end
WEBrick::HTTPRequest::MAX_HEADER_LENGTH = T.let(T.unsafe(nil), Integer)
class WEBrick::HTTPResponse
def initialize(config); end
def [](field); end
def []=(field, value); end
def body; end
def body=(_arg0); end
def chunked=(val); end
def chunked?; end
def config; end
def content_length; end
def content_length=(len); end
def content_type; end
def content_type=(type); end
def cookies; end
def each; end
def filename; end
def filename=(_arg0); end
def header; end
def http_version; end
def keep_alive; end
def keep_alive=(_arg0); end
def keep_alive?; end
def make_body_tempfile; end
def reason_phrase; end
def reason_phrase=(_arg0); end
def remove_body_tempfile; end
def request_http_version; end
def request_http_version=(_arg0); end
def request_method; end
def request_method=(_arg0); end
def request_uri; end
def request_uri=(_arg0); end
def send_body(socket); end
def send_header(socket); end
def send_response(socket); end
def sent_size; end
def set_error(ex, backtrace = T.unsafe(nil)); end
def set_redirect(status, url); end
def setup_header; end
def status; end
def status=(status); end
def status_line; end
def upgrade; end
def upgrade!(protocol); end
def upgrade=(_arg0); end
private
def _write_data(socket, data); end
def check_header(header_value); end
def error_body(backtrace, ex, host, port); end
def send_body_io(socket); end
def send_body_proc(socket); end
def send_body_string(socket); end
end
class WEBrick::HTTPResponse::ChunkedWrapper
def initialize(socket, resp); end
def <<(*buf); end
def write(buf); end
end
class WEBrick::HTTPServer < ::WEBrick::GenericServer
def initialize(config = T.unsafe(nil), default = T.unsafe(nil)); end
def access_log(config, req, res); end
def create_request(with_webrick_config); end
def create_response(with_webrick_config); end
def do_OPTIONS(req, res); end
def lookup_server(req); end
def mount(dir, servlet, *options); end
def mount_proc(dir, proc = T.unsafe(nil), &block); end
def run(sock); end
def search_servlet(path); end
def service(req, res); end
def umount(dir); end
def unmount(dir); end
def virtual_host(server); end
end
class WEBrick::HTTPServer::MountTable
def initialize; end
def [](dir); end
def []=(dir, val); end
def delete(dir); end
def scan(path); end
private
def compile; end
def normalize(dir); end
end
class WEBrick::HTTPServlet::AbstractServlet
def initialize(server, *options); end
def do_GET(req, res); end
def do_HEAD(req, res); end
def do_OPTIONS(req, res); end
def service(req, res); end
private
def redirect_to_directory_uri(req, res); end
class << self
def get_instance(server, *options); end
end
end
class WEBrick::HTTPServlet::CGIHandler < ::WEBrick::HTTPServlet::AbstractServlet
def initialize(server, name); end
def do_GET(req, res); end
def do_POST(req, res); end
end
WEBrick::HTTPServlet::CGIHandler::CGIRunnerArray = T.let(T.unsafe(nil), Array)
class WEBrick::HTTPServlet::DefaultFileHandler < ::WEBrick::HTTPServlet::AbstractServlet
def initialize(server, local_path); end
def do_GET(req, res); end
def make_partial_content(req, res, filename, filesize); end
def multipart_body(body, parts, boundary, mtype, filesize); end
def not_modified?(req, res, mtime, etag); end
def prepare_range(range, filesize); end
end
class WEBrick::HTTPServlet::ERBHandler < ::WEBrick::HTTPServlet::AbstractServlet
def initialize(server, name); end
def do_GET(req, res); end
def do_POST(req, res); end
private
def evaluate(erb, servlet_request, servlet_response); end
end
class WEBrick::HTTPServlet::FileHandler < ::WEBrick::HTTPServlet::AbstractServlet
def initialize(server, root, options = T.unsafe(nil), default = T.unsafe(nil)); end
def do_GET(req, res); end
def do_OPTIONS(req, res); end
def do_POST(req, res); end
def service(req, res); end
def set_filesystem_encoding(str); end
private
def call_callback(callback_name, req, res); end
def check_filename(req, res, name); end
def exec_handler(req, res); end
def get_handler(req, res); end
def nondisclosure_name?(name); end
def prevent_directory_traversal(req, res); end
def search_file(req, res, basename); end
def search_index_file(req, res); end
def set_dir_list(req, res); end
def set_filename(req, res); end
def shift_path_info(req, res, path_info, base = T.unsafe(nil)); end
def trailing_pathsep?(path); end
def windows_ambiguous_name?(name); end
class << self
def add_handler(suffix, handler); end
def remove_handler(suffix); end
end
end
class WEBrick::HTTPServlet::ProcHandler < ::WEBrick::HTTPServlet::AbstractServlet
def initialize(proc); end
def do_GET(request, response); end
def do_POST(request, response); end
def do_PUT(request, response); end
def get_instance(server, *options); end
end
module WEBrick::HTTPStatus
private
def client_error?(code); end
def error?(code); end
def info?(code); end
def reason_phrase(code); end
def redirect?(code); end
def server_error?(code); end
def success?(code); end
class << self
def [](code); end
def client_error?(code); end
def error?(code); end
def info?(code); end
def reason_phrase(code); end
def redirect?(code); end
def server_error?(code); end
def success?(code); end
end
end
class WEBrick::HTTPStatus::Status < ::StandardError
def code; end
def reason_phrase; end
def to_i; end
class << self
def code; end
def reason_phrase; end
end
end
module WEBrick::HTTPUtils
private
def _escape(str, regex); end
def _make_regex(str); end
def _make_regex!(str); end
def _unescape(str, regex); end
def dequote(str); end
def escape(str); end
def escape8bit(str); end
def escape_form(str); end
def escape_path(str); end
def load_mime_types(file); end
def mime_type(filename, mime_tab); end
def normalize_path(path); end
def parse_form_data(io, boundary); end
def parse_header(raw); end
def parse_query(str); end
def parse_qvalues(value); end
def parse_range_header(ranges_specifier); end
def quote(str); end
def split_header_value(str); end
def unescape(str); end
def unescape_form(str); end
class << self
def _escape(str, regex); end
def _make_regex(str); end
def _make_regex!(str); end
def _unescape(str, regex); end
def dequote(str); end
def escape(str); end
def escape8bit(str); end
def escape_form(str); end
def escape_path(str); end
def load_mime_types(file); end
def mime_type(filename, mime_tab); end
def normalize_path(path); end
def parse_form_data(io, boundary); end
def parse_header(raw); end
def parse_query(str); end
def parse_qvalues(value); end
def parse_range_header(ranges_specifier); end
def quote(str); end
def split_header_value(str); end
def unescape(str); end
def unescape_form(str); end
end
end
class WEBrick::HTTPUtils::FormData < ::String
def initialize(*args); end
def <<(str); end
def [](*key); end
def append_data(data); end
def each_data; end
def filename; end
def filename=(_arg0); end
def list; end
def name; end
def name=(_arg0); end
def next_data=(_arg0); end
def to_ary; end
def to_s; end
protected
def next_data; end
end
module WEBrick::Utils
private
def create_listeners(address, port); end
def getservername; end
def random_string(len); end
def set_close_on_exec(io); end
def set_non_blocking(io); end
def su(user); end
def timeout(seconds, exception = T.unsafe(nil)); end
class << self
def create_listeners(address, port); end
def getservername; end
def random_string(len); end
def set_close_on_exec(io); end
def set_non_blocking(io); end
def su(user); end
def timeout(seconds, exception = T.unsafe(nil)); end
end
end
class WEBrick::Utils::TimeoutHandler
include ::Singleton
extend ::Singleton::SingletonClassMethods
def initialize; end
def cancel(thread, id); end
def interrupt(thread, id, exception); end
def register(thread, time, exception); end
def terminate; end
private
def watch; end
def watcher; end
class << self
def cancel(id); end
def register(seconds, exception); end
def terminate; end
end
end

View File

@ -17,4 +17,9 @@ describe "brew", :integration_test do
# ActiveSupport inflections are slow to load, so we don't use them.
expect { ActiveSupport::Inflector }.to raise_error(NameError)
end
it "does not require Nokogiri" do
# The latest version of Nokogiri for Ruby 2.6 has multiple CVEs.
expect { Nokogiri }.to raise_error(NameError)
end
end

View File

@ -42,36 +42,20 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/coderay-1.1.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/highline-2.0.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/commander-4.6.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/connection_pool-2.4.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/did_you_mean-1.6.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/diff-lcs-1.5.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/docile-1.4.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/unf_ext-0.0.8.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unf_ext-0.0.8.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unf-0.1.4/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/domain_name-0.5.20190701/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/regexp_parser-2.8.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ecma-re-validator-0.4.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/elftools-1.2.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/hana-1.3.7/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/hpricot-0.8.6")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/hpricot-0.8.6/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/http-cookie-1.0.5/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/json-2.6.3")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json-2.6.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/uri_template-0.7.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/json_schemer-0.2.24/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/mime-types-data-3.2023.0218.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/mime-types-3.4.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/net-http-digest_auth-1.4.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/net-http-persistent-4.0.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/racc-1.6.2")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/racc-1.6.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/nokogiri-1.13.10-x86_64-darwin/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubyntlm-0.6.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/webrick-1.8.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/webrobots-0.1.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/mechanize-2.9.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/method_source-1.0.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/mustache-1.1.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel-1.23.0/lib")

View File

@ -1,266 +0,0 @@
# encoding: UTF-8
#
# = net/ntlm.rb
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
#++
require 'base64'
require 'openssl'
require 'openssl/digest'
require 'socket'
# Load Order is important here
require 'net/ntlm/exceptions'
require 'net/ntlm/field'
require 'net/ntlm/int16_le'
require 'net/ntlm/int32_le'
require 'net/ntlm/int64_le'
require 'net/ntlm/string'
require 'net/ntlm/field_set'
require 'net/ntlm/blob'
require 'net/ntlm/security_buffer'
require 'net/ntlm/message'
require 'net/ntlm/message/type0'
require 'net/ntlm/message/type1'
require 'net/ntlm/message/type2'
require 'net/ntlm/message/type3'
require 'net/ntlm/encode_util'
require 'net/ntlm/client'
require 'net/ntlm/channel_binding'
require 'net/ntlm/target_info'
module Net
module NTLM
LM_MAGIC = "KGS!@\#$%"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
class << self
# Valid format for LAN Manager hex digest portion: 32 hexadecimal characters.
LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
# Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters.
NT_LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
# Valid format for an NTLM hash composed of `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`.
DATA_REGEXP = /\A#{LAN_MANAGER_HEX_DIGEST_REGEXP}:#{NT_LAN_MANAGER_HEX_DIGEST_REGEXP}\z/
# Takes a string and determines whether it is a valid NTLM Hash
# @param [String] the string to validate
# @return [Boolean] whether or not the string is a valid NTLM hash
def is_ntlm_hash?(data)
decoded_data = data.dup
decoded_data = EncodeUtil.decode_utf16le(decoded_data)
if DATA_REGEXP.match(decoded_data)
true
else
false
end
end
# Conver the value to a 64-Bit Little Endian Int
# @param [String] val The string to convert
def pack_int64le(val)
[val & 0x00000000ffffffff, val >> 32].pack("V2")
end
# Builds an array of strings that are 7 characters long
# @param [String] str The string to split
# @api private
def split7(str)
s = str.dup
until s.empty?
(ret ||= []).push s.slice!(0, 7)
end
ret
end
# Not sure what this is doing
# @param [String] str String to generate keys for
# @api private
def gen_keys(str)
split7(str).map{ |str7|
bits = split7(str7.unpack("B*")[0]).inject('')\
{|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
[bits].pack("B*")
}
end
def apply_des(plain, keys)
dec = OpenSSL::Cipher.new("des-cbc").encrypt
dec.padding = 0
keys.map {|k|
dec.key = k
dec.update(plain) + dec.final
}
end
# Generates a Lan Manager Hash
# @param [String] password The password to base the hash on
def lm_hash(password)
keys = gen_keys password.upcase.ljust(14, "\0")
apply_des(LM_MAGIC, keys).join
end
# Generate a NTLM Hash
# @param [String] password The password to base the hash on
# @option opt :unicode (false) Unicode encode the password
def ntlm_hash(password, opt = {})
pwd = password.dup
unless opt[:unicode]
pwd = EncodeUtil.encode_utf16le(pwd)
end
OpenSSL::Digest::MD4.digest pwd
end
# Generate a NTLMv2 Hash
# @param [String] user The username
# @param [String] password The password
# @param [String] target The domain or workstation to authenticate to
# @option opt :unicode (false) Unicode encode the domain
def ntlmv2_hash(user, password, target, opt={})
if is_ntlm_hash? password
decoded_password = EncodeUtil.decode_utf16le(password)
ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
else
ntlmhash = ntlm_hash(password, opt)
end
userdomain = user.upcase + target
unless opt[:unicode]
userdomain = EncodeUtil.encode_utf16le(userdomain)
end
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
end
def lm_response(arg)
begin
hash = arg[:lm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
def ntlm_response(arg)
hash = arg[:ntlm_hash]
chal = arg[:challenge]
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
def ntlmv2_response(arg, opt = {})
begin
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
ti = arg[:target_info]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
if opt[:timestamp]
ts = opt[:timestamp]
else
ts = Time.now.to_i
end
# epoch -> milsec from Jan 1, 1601
ts = 10_000_000 * (ts + TIME_OFFSET)
blob = Blob.new
blob.timestamp = ts
blob.challenge = cc
blob.target_info = ti
bb = blob.serialize
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
def lmv2_response(arg, opt = {})
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end
def ntlm2_session(arg, opt = {})
begin
passwd_hash = arg[:ntlm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
keys = gen_keys(passwd_hash.ljust(21, "\0"))
session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
response = apply_des(session_hash, keys).join
[cc.ljust(24, "\0"), response]
end
end
end
end

View File

@ -1,28 +0,0 @@
module Net
module NTLM
BLOB_SIGN = 0x00000101
class Blob < FieldSet
int32LE :blob_signature, {:value => BLOB_SIGN}
int32LE :reserved, {:value => 0}
int64LE :timestamp, {:value => 0}
string :challenge, {:value => "", :size => 8}
int32LE :unknown1, {:value => 0}
string :target_info, {:value => "", :size => 0}
int32LE :unknown2, {:value => 0}
def parse(str, offset=0)
# 28 is the length of all fields before the variable-length
# target_info field.
if str.size > 28
enable(:target_info)
# Grab everything except the last 4 bytes (which will be :unknown2)
self[:target_info].value = str[28..-5]
end
super
end
end
end
end

View File

@ -1,65 +0,0 @@
module Net
module NTLM
class ChannelBinding
# Creates a ChannelBinding used for Extended Protection Authentication
# @see http://blogs.msdn.com/b/openspecification/archive/2013/03/26/ntlm-and-channel-binding-hash-aka-exteneded-protection-for-authentication.aspx
#
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing
# the outer TLS channel
# @return [NTLM::ChannelBinding] A ChannelBinding holding a token that can be
# embedded in a {Type3} message
def self.create(outer_channel)
new(outer_channel)
end
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing
# the outer TLS channel
def initialize(outer_channel)
@channel = outer_channel
@unique_prefix = 'tls-server-end-point'
@initiator_addtype = 0
@initiator_address_length = 0
@acceptor_addrtype = 0
@acceptor_address_length = 0
end
attr_reader :channel, :unique_prefix, :initiator_addtype
attr_reader :initiator_address_length, :acceptor_addrtype
attr_reader :acceptor_address_length
# Returns a channel binding hash acceptable for use as a AV_PAIR MsvAvChannelBindings
# field value as specified in the NTLM protocol
#
# @return [String] MD5 hash of gss_channel_bindings_struct
def channel_binding_token
@channel_binding_token ||= OpenSSL::Digest::MD5.new(gss_channel_bindings_struct).digest
end
def gss_channel_bindings_struct
@gss_channel_bindings_struct ||= begin
token = [initiator_addtype].pack('I')
token << [initiator_address_length].pack('I')
token << [acceptor_addrtype].pack('I')
token << [acceptor_address_length].pack('I')
token << [application_data.length].pack('I')
token << application_data
token
end
end
def channel_hash
@channel_hash ||= OpenSSL::Digest::SHA256.new(channel.to_der)
end
def application_data
@application_data ||= begin
data = unique_prefix
data << ':'
data << channel_hash.digest
data
end
end
end
end
end

View File

@ -1,65 +0,0 @@
module Net
module NTLM
class Client
DEFAULT_FLAGS = NTLM::FLAGS[:UNICODE] | NTLM::FLAGS[:OEM] |
NTLM::FLAGS[:SIGN] | NTLM::FLAGS[:SEAL] | NTLM::FLAGS[:REQUEST_TARGET] |
NTLM::FLAGS[:NTLM] | NTLM::FLAGS[:ALWAYS_SIGN] | NTLM::FLAGS[:NTLM2_KEY] |
NTLM::FLAGS[:KEY128] | NTLM::FLAGS[:KEY_EXCHANGE] | NTLM::FLAGS[:KEY56]
attr_reader :username, :password, :domain, :workstation, :flags
# @note All string parameters should be encoded in UTF-8. The proper
# final encoding for placing in the various {Message messages} will be
# chosen based on negotiation with the server.
#
# @param username [String]
# @param password [String]
# @option opts [String] :domain where we're authenticating to
# @option opts [String] :workstation local workstation name
# @option opts [Fixnum] :flags (DEFAULT_FLAGS) see Net::NTLM::Message::Type1.flag
def initialize(username, password, opts = {})
@username = username
@password = password
@domain = opts[:domain] || nil
@workstation = opts[:workstation] || nil
@flags = opts[:flags] || DEFAULT_FLAGS
end
# @return [NTLM::Message]
def init_context(resp = nil, channel_binding = nil)
if resp.nil?
@session = nil
type1_message
else
@session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
@session.authenticate!
end
end
# @return [Client::Session]
def session
@session
end
def session_key
@session.exported_session_key
end
private
# @return [Message::Type1]
def type1_message
type1 = Message::Type1.new
type1[:flag].value = flags
type1.domain = domain if domain
type1.workstation = workstation if workstation
type1
end
end
end
end
require "net/ntlm/client/session"

View File

@ -1,237 +0,0 @@
module Net
module NTLM
class Client::Session
VERSION_MAGIC = "\x01\x00\x00\x00"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
CLIENT_TO_SERVER_SIGNING = "session key to client-to-server signing key magic constant\0"
SERVER_TO_CLIENT_SIGNING = "session key to server-to-client signing key magic constant\0"
CLIENT_TO_SERVER_SEALING = "session key to client-to-server sealing key magic constant\0"
SERVER_TO_CLIENT_SEALING = "session key to server-to-client sealing key magic constant\0"
attr_reader :client, :challenge_message, :channel_binding
# @param client [Net::NTLM::Client] the client instance
# @param challenge_message [Net::NTLM::Message::Type2] server message
def initialize(client, challenge_message, channel_binding = nil)
@client = client
@challenge_message = challenge_message
@channel_binding = channel_binding
end
# Generate an NTLMv2 AUTHENTICATE_MESSAGE
# @see http://msdn.microsoft.com/en-us/library/cc236643.aspx
# @return [Net::NTLM::Message::Type3]
def authenticate!
calculate_user_session_key!
type3_opts = {
:lm_response => lmv2_resp,
:ntlm_response => ntlmv2_resp,
:domain => domain,
:user => username,
:workstation => workstation,
:flag => (challenge_message.flag & client.flags)
}
t3 = Message::Type3.create type3_opts
if negotiate_key_exchange?
t3.enable(:session_key)
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = user_session_key
sk = rc4.update exported_session_key
sk << rc4.final
t3.session_key = sk
end
t3
end
def exported_session_key
@exported_session_key ||=
begin
if negotiate_key_exchange?
OpenSSL::Cipher.new("rc4").random_key
else
user_session_key
end
end
end
def sign_message(message)
seq = sequence
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, client_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = client_cipher.update sig
sig << client_cipher.final
end
"#{VERSION_MAGIC}#{sig}#{seq}"
end
def verify_signature(signature, message)
seq = signature[-4..-1]
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, server_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = server_cipher.update sig
sig << server_cipher.final
end
"#{VERSION_MAGIC}#{sig}#{seq}" == signature
end
def seal_message(message)
emessage = client_cipher.update(message)
emessage + client_cipher.final
end
def unseal_message(emessage)
message = server_cipher.update(emessage)
message + server_cipher.final
end
private
def user_session_key
@user_session_key ||= nil
end
def sequence
[raw_sequence].pack("V*")
end
def raw_sequence
if defined? @raw_sequence
@raw_sequence += 1
else
@raw_sequence = 0
end
end
def client_sign_key
@client_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SIGNING}"
end
def server_sign_key
@server_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SIGNING}"
end
def client_seal_key
@client_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SEALING}"
end
def server_seal_key
@server_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SEALING}"
end
def client_cipher
@client_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = client_seal_key
rc4
end
end
def server_cipher
@server_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.decrypt
rc4.key = server_seal_key
rc4
end
end
def client_challenge
@client_challenge ||= NTLM.pack_int64le(rand(MAX64))
end
def server_challenge
@server_challenge ||= challenge_message[:challenge].serialize
end
# epoch -> milsec from Jan 1, 1601
# @see http://support.microsoft.com/kb/188768
def timestamp
@timestamp ||= 10_000_000 * (Time.now.to_i + TIME_OFFSET)
end
def use_oem_strings?
challenge_message.has_flag? :OEM
end
def negotiate_key_exchange?
challenge_message.has_flag? :KEY_EXCHANGE
end
def username
oem_or_unicode_str client.username
end
def password
oem_or_unicode_str client.password
end
def workstation
(client.workstation ? oem_or_unicode_str(client.workstation) : "")
end
def domain
(client.domain ? oem_or_unicode_str(client.domain) : "")
end
def oem_or_unicode_str(str)
if use_oem_strings?
NTLM::EncodeUtil.decode_utf16le str
else
NTLM::EncodeUtil.encode_utf16le str
end
end
def ntlmv2_hash
@ntlmv2_hash ||= NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
end
def calculate_user_session_key!
@user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
end
def lmv2_resp
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + client_challenge) + client_challenge
end
def ntlmv2_resp
nt_proof_str + blob
end
def nt_proof_str
@nt_proof_str ||= OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + blob)
end
def blob
@blob ||=
begin
b = Blob.new
b.timestamp = timestamp
b.challenge = client_challenge
b.target_info = target_info
b.serialize
end
end
def target_info
@target_info ||= begin
if channel_binding
t = Net::NTLM::TargetInfo.new(challenge_message.target_info)
av_id = Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS
t.av_pairs[av_id] = channel_binding.channel_binding_token
t.to_s
else
challenge_message.target_info
end
end
end
end
end
end

View File

@ -1,48 +0,0 @@
module Net
module NTLM
class EncodeUtil
if RUBY_VERSION == "1.8.7"
require "kconv"
# Decode a UTF16 string to a ASCII string
# @param [String] str The string to convert
def self.decode_utf16le(str)
Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
end
# Encodes a ASCII string to a UTF16 string
# @param [String] str The string to convert
def self.encode_utf16le(str)
swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
end
# Taggle the strings endianness between big/little and little/big
# @param [String] str The string to swap the endianness on
def self.swap16(str)
str.unpack("v*").pack("n*")
end
else # Use native 1.9 string encoding functions
# Decode a UTF16 string to a ASCII string
# @param [String] str The string to convert
def self.decode_utf16le(str)
str = str.dup.force_encoding(Encoding::UTF_16LE)
str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
end
# Encodes a ASCII string to a UTF16 string
# @param [String] str The string to convert
# @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
# encodings. This library uses string contatination to build the packet bytes. The end result is that
# you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
# the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
# concatination works seamlessly.
def self.encode_utf16le(str)
str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8')
end
end
end
end
end

View File

@ -1,14 +0,0 @@
module Net
module NTLM
class NtlmError < StandardError; end
class InvalidTargetDataError < NtlmError
attr_reader :data
def initialize(msg, data)
@data = data
super(msg)
end
end
end
end

View File

@ -1,35 +0,0 @@
module Net
module NTLM
# base classes for primitives
# @private
class Field
attr_accessor :active, :value
def initialize(opts)
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = opts[:size].nil? ? 0 : opts[:size]
end
def size
@active ? @size : 0
end
# Serializer function for field data
# Exists in this class to be overridden by child classes
def serialize
raise NotImplementedError
end
# Parser function for field data
# Exists in this class to be overridden by child classes
def parse(str, offset=0)
raise NotImplementedError
end
end
end
end

View File

@ -1,129 +0,0 @@
module Net
module NTLM
# base class of data structure
class FieldSet
class << FieldSet
# @macro string_security_buffer
# @method $1
# @method $1=
# @return [String]
def string(name, opts)
add_field(name, Net::NTLM::String, opts)
end
# @macro int16le_security_buffer
# @method $1
# @method $1=
# @return [Int16LE]
def int16LE(name, opts)
add_field(name, Net::NTLM::Int16LE, opts)
end
# @macro int32le_security_buffer
# @method $1
# @method $1=
# @return [Int32LE]
def int32LE(name, opts)
add_field(name, Net::NTLM::Int32LE, opts)
end
# @macro int64le_security_buffer
# @method $1
# @method $1=
# @return [Int64]
def int64LE(name, opts)
add_field(name, Net::NTLM::Int64LE, opts)
end
# @macro security_buffer
# @method $1
# @method $1=
# @return [SecurityBuffer]
def security_buffer(name, opts)
add_field(name, Net::NTLM::SecurityBuffer, opts)
end
def prototypes
@proto
end
def names
return [] if @proto.nil?
@proto.map{|n, t, o| n}
end
def types
return [] if @proto.nil?
@proto.map{|n, t, o| t}
end
def opts
return [] if @proto.nil?
@proto.map{|n, t, o| o}
end
private
def add_field(name, type, opts)
(@proto ||= []).push [name, type, opts]
define_accessor name
end
def define_accessor(name)
module_eval(<<-End, __FILE__, __LINE__ + 1)
def #{name}
self['#{name}'].value
end
def #{name}=(val)
self['#{name}'].value = val
end
End
end
end
def initialize
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
end
def parse(str, offset=0)
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
end
def serialize
@alist.map{|n, f| f.serialize }.join
end
def size
@alist.inject(0){|sum, a| sum += a[1].size}
end
def [](name)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1]
end
def []=(name, val)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1] = val
end
def enable(name)
self[name].active = true
end
def disable(name)
self[name].active = false
end
def has_disabled_fields?
@alist.any? { |name, field| !field.active }
end
end
end
end

View File

@ -1,26 +0,0 @@
module Net
module NTLM
class Int16LE < Field
def initialize(opt)
super(opt)
@size = 2
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size].unpack("v")[0]
@size
else
0
end
end
def serialize
[@value].pack("v")
end
end
end
end

View File

@ -1,25 +0,0 @@
module Net
module NTLM
class Int32LE < Field
def initialize(opt)
super(opt)
@size = 4
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str.slice(offset, @size).unpack("V")[0]
@size
else
0
end
end
def serialize
[@value].pack("V") if @active
end
end
end
end

View File

@ -1,26 +0,0 @@
module Net
module NTLM
class Int64LE < Field
def initialize(opt)
super(opt)
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
d, u = str.slice(offset, @size).unpack("V2")
@value = (u * 0x100000000 + d)
@size
else
0
end
end
def serialize
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
end
end
end
end

View File

@ -1,129 +0,0 @@
module Net
module NTLM
SSP_SIGN = "NTLMSSP\0"
FLAGS = {
:UNICODE => 0x00000001,
:OEM => 0x00000002,
:REQUEST_TARGET => 0x00000004,
:MBZ9 => 0x00000008,
:SIGN => 0x00000010,
:SEAL => 0x00000020,
:NEG_DATAGRAM => 0x00000040,
:NETWARE => 0x00000100,
:NTLM => 0x00000200,
:NEG_NT_ONLY => 0x00000400,
:MBZ7 => 0x00000800,
:DOMAIN_SUPPLIED => 0x00001000,
:WORKSTATION_SUPPLIED => 0x00002000,
:LOCAL_CALL => 0x00004000,
:ALWAYS_SIGN => 0x00008000,
:TARGET_TYPE_DOMAIN => 0x00010000,
:NTLM2_KEY => 0x00080000,
:TARGET_INFO => 0x00800000,
:KEY128 => 0x20000000,
:KEY_EXCHANGE => 0x40000000,
:KEY56 => 0x80000000
}.freeze
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
DEFAULT_FLAGS = {
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
:TYPE2 => FLAGS[:UNICODE],
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
}
# @private false
class Message < FieldSet
class << Message
def parse(str)
m = Type0.new
m.parse(str)
case m.type
when 1
t = Type1.new.parse(str)
when 2
t = Type2.new.parse(str)
when 3
t = Type3.new.parse(str)
else
raise ArgumentError, "unknown type: #{m.type}"
end
t
end
def decode64(str)
parse(Base64.decode64(str))
end
end
# @return [self]
def parse(str)
super
while has_disabled_fields? && serialize.size < str.size
# enable the next disabled field
self.class.names.find { |name| !self[name].active && enable(name) }
super
end
self
end
def has_flag?(flag)
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
end
def set_flag(flag)
self[:flag].value |= FLAGS[flag]
end
def dump_flags
FLAG_KEYS.each{ |k| print(k, "=", has_flag?(k), "\n") }
end
def serialize
deflag
super + security_buffers.map{|n, f| f.value}.join
end
def encode64
Base64.encode64(serialize).gsub(/\n/, '')
end
def decode64(str)
parse(Base64.decode64(str))
end
alias head_size size
def data_size
security_buffers.inject(0){|sum, a| sum += a[1].data_size}
end
def size
head_size + data_size
end
def security_buffers
@alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
end
def deflag
security_buffers.inject(head_size){|cur, a|
a[1].offset = cur
cur += a[1].data_size
}
end
def data_edge
security_buffers.map{ |n, f| f.active ? f.offset : size}.min
end
end
end
end

View File

@ -1,16 +0,0 @@
module Net
module NTLM
class Message
# sub class definitions
class Type0 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 0}
end
end
end
end

View File

@ -1,18 +0,0 @@
module Net
module NTLM
class Message
# @private false
class Type1 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 1}
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
security_buffer :domain, {:value => ""}
security_buffer :workstation, {:value => Socket.gethostname }
string :os_version, {:size => 8, :value => "", :active => false }
end
end
end
end

View File

@ -1,102 +0,0 @@
module Net
module NTLM
class Message
# @private false
class Type2 < Message
string :sign, { :size => 8, :value => SSP_SIGN }
int32LE :type, { :value => 2 }
security_buffer :target_name, { :size => 0, :value => "" }
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
int64LE :challenge, { :value => 0}
int64LE :context, { :value => 0, :active => false }
security_buffer :target_info, { :value => "", :active => false }
string :os_version, { :size => 8, :value => "", :active => false }
# Generates a Type 3 response based on the Type 2 Information
# @return [Type3]
# @option arg [String] :username The username to authenticate with
# @option arg [String] :password The user's password
# @option arg [String] :domain ('') The domain to authenticate to
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
# @note An empty :domain option authenticates to the local machine.
# @note The :use_default_target has precedence over the :domain option
def response(arg, opt = {})
usr = arg[:user]
pwd = arg[:password]
domain = arg[:domain] ? arg[:domain].upcase : ""
if usr.nil? or pwd.nil?
raise ArgumentError, "user and password have to be supplied"
end
if opt[:workstation]
ws = opt[:workstation]
else
ws = Socket.gethostname
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
opt[:client_challenge] = cc
if has_flag?(:OEM) and opt[:unicode]
usr = NTLM::EncodeUtil.decode_utf16le(usr)
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
ws = NTLM::EncodeUtil.decode_utf16le(ws)
domain = NTLM::EncodeUtil.decode_utf16le(domain)
opt[:unicode] = false
end
if has_flag?(:UNICODE) and !opt[:unicode]
usr = NTLM::EncodeUtil.encode_utf16le(usr)
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
ws = NTLM::EncodeUtil.encode_utf16le(ws)
domain = NTLM::EncodeUtil.encode_utf16le(domain)
opt[:unicode] = true
end
if opt[:use_default_target]
domain = self.target_name
end
ti = self.target_info
chal = self[:challenge].serialize
if opt[:ntlmv2]
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
lm_res = NTLM::lmv2_response(ar, opt)
ntlm_res = NTLM::ntlmv2_response(ar, opt)
elsif has_flag?(:NTLM2_KEY)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
else
ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
lm_res = NTLM::lm_response(ar)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
ntlm_res = NTLM::ntlm_response(ar)
end
Type3.create({
:lm_response => lm_res,
:ntlm_response => ntlm_res,
:domain => domain,
:user => usr,
:workstation => ws,
:flag => self.flag
})
end
end
end
end
end

View File

@ -1,131 +0,0 @@
module Net
module NTLM
class Message
# @private false
class Type3 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 3}
security_buffer :lm_response, {:value => ""}
security_buffer :ntlm_response, {:value => ""}
security_buffer :domain, {:value => ""}
security_buffer :user, {:value => ""}
security_buffer :workstation, {:value => ""}
security_buffer :session_key, {:value => "", :active => false }
int32LE :flag, {:value => 0, :active => false }
string :os_version, {:size => 8, :active => false }
class << Type3
# Builds a Type 3 packet
# @note All options must be properly encoded with either unicode or oem encoding
# @return [Type3]
# @option arg [String] :lm_response The LM hash
# @option arg [String] :ntlm_response The NTLM hash
# @option arg [String] :domain The domain to authenticate to
# @option arg [String] :workstation The name of the calling workstation
# @option arg [String] :session_key The session key
# @option arg [Integer] :flag Flags for the packet
def create(arg, opt ={})
t = new
t.lm_response = arg[:lm_response]
t.ntlm_response = arg[:ntlm_response]
t.domain = arg[:domain]
t.user = arg[:user]
if arg[:workstation]
t.workstation = arg[:workstation]
end
if arg[:session_key]
t.enable(:session_key)
t.session_key = arg[:session_key]
end
if arg[:flag]
t.enable(:session_key)
t.enable(:flag)
t.flag = arg[:flag]
end
t
end
end
# @param server_challenge (see #password?)
def blank_password?(server_challenge)
password?('', server_challenge)
end
# @param password [String]
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
# {Type2} message for which this object is a response.
# @return [true] if +password+ was the password used to generate this
# {Type3} message
# @return [false] otherwise
def password?(password, server_challenge)
case ntlm_version
when :ntlm2_session
ntlm2_session_password?(password, server_challenge)
when :ntlmv2
ntlmv2_password?(password, server_challenge)
else
raise
end
end
# @return [Symbol]
def ntlm_version
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
:ntlm2_session
elsif ntlm_response.size == 24
:ntlmv1
elsif ntlm_response.size > 24
:ntlmv2
end
end
private
def ntlm2_session_password?(password, server_challenge)
hash = ntlm_response
_lm, empty_hash = NTLM.ntlm2_session(
{
:ntlm_hash => NTLM.ntlm_hash(password),
:challenge => server_challenge,
},
{
:client_challenge => lm_response[0,8]
}
)
hash == empty_hash
end
def ntlmv2_password?(password, server_challenge)
# The first 16 bytes of the ntlm_response are the HMAC of the blob
# that follows it.
blob = Blob.new
blob.parse(ntlm_response[16..-1])
empty_hash = NTLM.ntlmv2_response(
{
# user and domain came from the serialized data here, so
# they're already unicode
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
:challenge => server_challenge,
:target_info => blob.target_info
},
{
:client_challenge => blob.challenge,
# The blob's timestamp is already in milliseconds since 1601,
# so convert it back to epoch time first
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
}
)
empty_hash == ntlm_response
end
end
end
end
end

View File

@ -1,48 +0,0 @@
module Net
module NTLM
class SecurityBuffer < FieldSet
int16LE :length, {:value => 0}
int16LE :allocated, {:value => 0}
int32LE :offset, {:value => 0}
attr_accessor :active
def initialize(opts={})
super()
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
super(str, offset)
@value = str[self.offset, self.length]
@size
else
0
end
end
def serialize
super if @active
end
def value
@value
end
def value=(val)
@value = val
self.length = self.allocated = val.size
end
def data_size
@active ? @value.size : 0
end
end
end
end

View File

@ -1,35 +0,0 @@
module Net
module NTLM
class String < Field
def initialize(opts)
super(opts)
@size = opts[:size]
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size]
@size
else
0
end
end
def serialize
if @active
@value.to_s
else
""
end
end
def value=(val)
@value = val
@size = @value.nil? ? 0 : @value.size
@active = (@size > 0)
end
end
end
end

View File

@ -1,89 +0,0 @@
module Net
module NTLM
# Represents a list of AV_PAIR structures
# @see https://msdn.microsoft.com/en-us/library/cc236646.aspx
class TargetInfo
# Allowed AvId values for an AV_PAIR
MSV_AV_EOL = "\x00\x00".freeze
MSV_AV_NB_COMPUTER_NAME = "\x01\x00".freeze
MSV_AV_NB_DOMAIN_NAME = "\x02\x00".freeze
MSV_AV_DNS_COMPUTER_NAME = "\x03\x00".freeze
MSV_AV_DNS_DOMAIN_NAME = "\x04\x00".freeze
MSV_AV_DNS_TREE_NAME = "\x05\x00".freeze
MSV_AV_FLAGS = "\x06\x00".freeze
MSV_AV_TIMESTAMP = "\x07\x00".freeze
MSV_AV_SINGLE_HOST = "\x08\x00".freeze
MSV_AV_TARGET_NAME = "\x09\x00".freeze
MSV_AV_CHANNEL_BINDINGS = "\x0A\x00".freeze
# @param av_pair_sequence [String] AV_PAIR list from challenge message
def initialize(av_pair_sequence)
@av_pairs = read_pairs(av_pair_sequence)
end
attr_reader :av_pairs
def to_s
result = ''
av_pairs.each do |k,v|
result << k
result << [v.length].pack('S')
result << v
end
result << Net::NTLM::TargetInfo::MSV_AV_EOL
result << [0].pack('S')
result.force_encoding(Encoding::ASCII_8BIT)
end
private
VALID_PAIR_ID = [
MSV_AV_EOL,
MSV_AV_NB_COMPUTER_NAME,
MSV_AV_NB_DOMAIN_NAME,
MSV_AV_DNS_COMPUTER_NAME,
MSV_AV_DNS_DOMAIN_NAME,
MSV_AV_DNS_TREE_NAME,
MSV_AV_FLAGS,
MSV_AV_TIMESTAMP,
MSV_AV_SINGLE_HOST,
MSV_AV_TARGET_NAME,
MSV_AV_CHANNEL_BINDINGS
].freeze
def read_pairs(av_pair_sequence)
offset = 0
result = {}
return result if av_pair_sequence.nil?
until offset >= av_pair_sequence.length
id = av_pair_sequence[offset..offset+1]
unless VALID_PAIR_ID.include?(id)
raise Net::NTLM::InvalidTargetDataError.new(
"Invalid AvId #{to_hex(id)} in AV_PAIR structure",
av_pair_sequence
)
end
length = av_pair_sequence[offset+2..offset+3].unpack('S')[0].to_i
if length > 0
value = av_pair_sequence[offset+4..offset+4+length-1]
result[id] = value
end
offset += 4 + length
end
result
end
def to_hex(str)
return nil if str.nil?
str.bytes.map {|b| '0x' + b.to_s(16).rjust(2,'0').upcase}.join('-')
end
end
end
end

View File

@ -1,11 +0,0 @@
module Net
module NTLM
# @private
module VERSION
MAJOR = 0
MINOR = 6
TINY = 3
STRING = [MAJOR, MINOR, TINY].join('.')
end
end
end

View File

@ -1 +0,0 @@
require 'net/ntlm'

View File

@ -1,232 +0,0 @@
# frozen_string_literal: false
##
# = WEB server toolkit.
#
# WEBrick is an HTTP server toolkit that can be configured as an HTTPS server,
# a proxy server, and a virtual-host server. WEBrick features complete
# logging of both server operations and HTTP access. WEBrick supports both
# basic and digest authentication in addition to algorithms not in RFC 2617.
#
# A WEBrick server can be composed of multiple WEBrick servers or servlets to
# provide differing behavior on a per-host or per-path basis. WEBrick
# includes servlets for handling CGI scripts, ERB pages, Ruby blocks and
# directory listings.
#
# WEBrick also includes tools for daemonizing a process and starting a process
# at a higher privilege level and dropping permissions.
#
# == Security
#
# *Warning:* WEBrick is not recommended for production. It only implements
# basic security checks.
#
# == Starting an HTTP server
#
# To create a new WEBrick::HTTPServer that will listen to connections on port
# 8000 and serve documents from the current user's public_html folder:
#
# require 'webrick'
#
# root = File.expand_path '~/public_html'
# server = WEBrick::HTTPServer.new :Port => 8000, :DocumentRoot => root
#
# To run the server you will need to provide a suitable shutdown hook as
# starting the server blocks the current thread:
#
# trap 'INT' do server.shutdown end
#
# server.start
#
# == Custom Behavior
#
# The easiest way to have a server perform custom operations is through
# WEBrick::HTTPServer#mount_proc. The block given will be called with a
# WEBrick::HTTPRequest with request info and a WEBrick::HTTPResponse which
# must be filled in appropriately:
#
# server.mount_proc '/' do |req, res|
# res.body = 'Hello, world!'
# end
#
# Remember that +server.mount_proc+ must precede +server.start+.
#
# == Servlets
#
# Advanced custom behavior can be obtained through mounting a subclass of
# WEBrick::HTTPServlet::AbstractServlet. Servlets provide more modularity
# when writing an HTTP server than mount_proc allows. Here is a simple
# servlet:
#
# class Simple < WEBrick::HTTPServlet::AbstractServlet
# def do_GET request, response
# status, content_type, body = do_stuff_with request
#
# response.status = 200
# response['Content-Type'] = 'text/plain'
# response.body = 'Hello, World!'
# end
# end
#
# To initialize the servlet you mount it on the server:
#
# server.mount '/simple', Simple
#
# See WEBrick::HTTPServlet::AbstractServlet for more details.
#
# == Virtual Hosts
#
# A server can act as a virtual host for multiple host names. After creating
# the listening host, additional hosts that do not listen can be created and
# attached as virtual hosts:
#
# server = WEBrick::HTTPServer.new # ...
#
# vhost = WEBrick::HTTPServer.new :ServerName => 'vhost.example',
# :DoNotListen => true, # ...
# vhost.mount '/', ...
#
# server.virtual_host vhost
#
# If no +:DocumentRoot+ is provided and no servlets or procs are mounted on the
# main server it will return 404 for all URLs.
#
# == HTTPS
#
# To create an HTTPS server you only need to enable SSL and provide an SSL
# certificate name:
#
# require 'webrick'
# require 'webrick/https'
#
# cert_name = [
# %w[CN localhost],
# ]
#
# server = WEBrick::HTTPServer.new(:Port => 8000,
# :SSLEnable => true,
# :SSLCertName => cert_name)
#
# This will start the server with a self-generated self-signed certificate.
# The certificate will be changed every time the server is restarted.
#
# To create a server with a pre-determined key and certificate you can provide
# them:
#
# require 'webrick'
# require 'webrick/https'
# require 'openssl'
#
# cert = OpenSSL::X509::Certificate.new File.read '/path/to/cert.pem'
# pkey = OpenSSL::PKey::RSA.new File.read '/path/to/pkey.pem'
#
# server = WEBrick::HTTPServer.new(:Port => 8000,
# :SSLEnable => true,
# :SSLCertificate => cert,
# :SSLPrivateKey => pkey)
#
# == Proxy Server
#
# WEBrick can act as a proxy server:
#
# require 'webrick'
# require 'webrick/httpproxy'
#
# proxy = WEBrick::HTTPProxyServer.new :Port => 8000
#
# trap 'INT' do proxy.shutdown end
#
# See WEBrick::HTTPProxy for further details including modifying proxied
# responses.
#
# == Basic and Digest authentication
#
# WEBrick provides both Basic and Digest authentication for regular and proxy
# servers. See WEBrick::HTTPAuth, WEBrick::HTTPAuth::BasicAuth and
# WEBrick::HTTPAuth::DigestAuth.
#
# == WEBrick as a daemonized Web Server
#
# WEBrick can be run as a daemonized server for small loads.
#
# === Daemonizing
#
# To start a WEBrick server as a daemon simple run WEBrick::Daemon.start
# before starting the server.
#
# === Dropping Permissions
#
# WEBrick can be started as one user to gain permission to bind to port 80 or
# 443 for serving HTTP or HTTPS traffic then can drop these permissions for
# regular operation. To listen on all interfaces for HTTP traffic:
#
# sockets = WEBrick::Utils.create_listeners nil, 80
#
# Then drop privileges:
#
# WEBrick::Utils.su 'www'
#
# Then create a server that does not listen by default:
#
# server = WEBrick::HTTPServer.new :DoNotListen => true, # ...
#
# Then overwrite the listening sockets with the port 80 sockets:
#
# server.listeners.replace sockets
#
# === Logging
#
# WEBrick can separately log server operations and end-user access. For
# server operations:
#
# log_file = File.open '/var/log/webrick.log', 'a+'
# log = WEBrick::Log.new log_file
#
# For user access logging:
#
# access_log = [
# [log_file, WEBrick::AccessLog::COMBINED_LOG_FORMAT],
# ]
#
# server = WEBrick::HTTPServer.new :Logger => log, :AccessLog => access_log
#
# See WEBrick::AccessLog for further log formats.
#
# === Log Rotation
#
# To rotate logs in WEBrick on a HUP signal (like syslogd can send), open the
# log file in 'a+' mode (as above) and trap 'HUP' to reopen the log file:
#
# trap 'HUP' do log_file.reopen '/path/to/webrick.log', 'a+'
#
# == Copyright
#
# Author: IPR -- Internet Programming with Ruby -- writers
#
# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#--
# $IPR: webrick.rb,v 1.12 2002/10/01 17:16:31 gotoyuzo Exp $
module WEBrick
end
require 'webrick/compat.rb'
require 'webrick/version.rb'
require 'webrick/config.rb'
require 'webrick/log.rb'
require 'webrick/server.rb'
require_relative 'webrick/utils.rb'
require 'webrick/accesslog'
require 'webrick/htmlutils.rb'
require 'webrick/httputils.rb'
require 'webrick/cookie.rb'
require 'webrick/httpversion.rb'
require 'webrick/httpstatus.rb'
require 'webrick/httprequest.rb'
require 'webrick/httpresponse.rb'
require 'webrick/httpserver.rb'
require 'webrick/httpservlet.rb'
require 'webrick/httpauth.rb'

View File

@ -1,157 +0,0 @@
# frozen_string_literal: true
#--
# accesslog.rb -- Access log handling utilities
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2002 keita yamaguchi
# Copyright (c) 2002 Internet Programming with Ruby writers
#
# $IPR: accesslog.rb,v 1.1 2002/10/01 17:16:32 gotoyuzo Exp $
module WEBrick
##
# AccessLog provides logging to various files in various formats.
#
# Multiple logs may be written to at the same time:
#
# access_log = [
# [$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT],
# [$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT],
# ]
#
# server = WEBrick::HTTPServer.new :AccessLog => access_log
#
# Custom log formats may be defined. WEBrick::AccessLog provides a subset
# of the formatting from Apache's mod_log_config
# http://httpd.apache.org/docs/mod/mod_log_config.html#formats. See
# AccessLog::setup_params for a list of supported options
module AccessLog
##
# Raised if a parameter such as %e, %i, %o or %n is used without fetching
# a specific field.
class AccessLogError < StandardError; end
##
# The Common Log Format's time format
CLF_TIME_FORMAT = "[%d/%b/%Y:%H:%M:%S %Z]"
##
# Common Log Format
COMMON_LOG_FORMAT = "%h %l %u %t \"%r\" %s %b"
##
# Short alias for Common Log Format
CLF = COMMON_LOG_FORMAT
##
# Referer Log Format
REFERER_LOG_FORMAT = "%{Referer}i -> %U"
##
# User-Agent Log Format
AGENT_LOG_FORMAT = "%{User-Agent}i"
##
# Combined Log Format
COMBINED_LOG_FORMAT = "#{CLF} \"%{Referer}i\" \"%{User-agent}i\""
module_function
# This format specification is a subset of mod_log_config of Apache:
#
# %a:: Remote IP address
# %b:: Total response size
# %e{variable}:: Given variable in ENV
# %f:: Response filename
# %h:: Remote host name
# %{header}i:: Given request header
# %l:: Remote logname, always "-"
# %m:: Request method
# %{attr}n:: Given request attribute from <tt>req.attributes</tt>
# %{header}o:: Given response header
# %p:: Server's request port
# %{format}p:: The canonical port of the server serving the request or the
# actual port or the client's actual port. Valid formats are
# canonical, local or remote.
# %q:: Request query string
# %r:: First line of the request
# %s:: Request status
# %t:: Time the request was received
# %T:: Time taken to process the request
# %u:: Remote user from auth
# %U:: Unparsed URI
# %%:: Literal %
def setup_params(config, req, res)
params = Hash.new("")
params["a"] = req.peeraddr[3]
params["b"] = res.sent_size
params["e"] = ENV
params["f"] = res.filename || ""
params["h"] = req.peeraddr[2]
params["i"] = req
params["l"] = "-"
params["m"] = req.request_method
params["n"] = req.attributes
params["o"] = res
params["p"] = req.port
params["q"] = req.query_string
params["r"] = req.request_line.sub(/\x0d?\x0a\z/o, '')
params["s"] = res.status # won't support "%>s"
params["t"] = req.request_time
params["T"] = Time.now - req.request_time
params["u"] = req.user || "-"
params["U"] = req.unparsed_uri
params["v"] = config[:ServerName]
params
end
##
# Formats +params+ according to +format_string+ which is described in
# setup_params.
def format(format_string, params)
format_string.gsub(/\%(?:\{(.*?)\})?>?([a-zA-Z%])/){
param, spec = $1, $2
case spec[0]
when ?e, ?i, ?n, ?o
raise AccessLogError,
"parameter is required for \"#{spec}\"" unless param
(param = params[spec][param]) ? escape(param) : "-"
when ?t
params[spec].strftime(param || CLF_TIME_FORMAT)
when ?p
case param
when 'remote'
escape(params["i"].peeraddr[1].to_s)
else
escape(params["p"].to_s)
end
when ?%
"%"
else
escape(params[spec].to_s)
end
}
end
##
# Escapes control characters in +data+
def escape(data)
data = data.gsub(/[[:cntrl:]\\]+/) {$&.dump[1...-1]}
data.untaint if RUBY_VERSION < '2.7'
data
end
end
end

View File

@ -1,313 +0,0 @@
# frozen_string_literal: true
#
# cgi.rb -- Yet another CGI library
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $Id$
require_relative "httprequest"
require_relative "httpresponse"
require_relative "config"
require "stringio"
module WEBrick
# A CGI library using WEBrick requests and responses.
#
# Example:
#
# class MyCGI < WEBrick::CGI
# def do_GET req, res
# res.body = 'it worked!'
# res.status = 200
# end
# end
#
# MyCGI.new.start
class CGI
# The CGI error exception class
CGIError = Class.new(StandardError)
##
# The CGI configuration. This is based on WEBrick::Config::HTTP
attr_reader :config
##
# The CGI logger
attr_reader :logger
##
# Creates a new CGI interface.
#
# The first argument in +args+ is a configuration hash which would update
# WEBrick::Config::HTTP.
#
# Any remaining arguments are stored in the <code>@options</code> instance
# variable for use by a subclass.
def initialize(*args)
if defined?(MOD_RUBY)
unless ENV.has_key?("GATEWAY_INTERFACE")
Apache.request.setup_cgi_env
end
end
if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"]
httpv = $1
end
@config = WEBrick::Config::HTTP.dup.update(
:ServerSoftware => ENV["SERVER_SOFTWARE"] || "null",
:HTTPVersion => HTTPVersion.new(httpv || "1.0"),
:RunOnCGI => true, # to detect if it runs on CGI.
:NPH => false # set true to run as NPH script.
)
if config = args.shift
@config.update(config)
end
@config[:Logger] ||= WEBrick::BasicLog.new($stderr)
@logger = @config[:Logger]
@options = args
end
##
# Reads +key+ from the configuration
def [](key)
@config[key]
end
##
# Starts the CGI process with the given environment +env+ and standard
# input and output +stdin+ and +stdout+.
def start(env=ENV, stdin=$stdin, stdout=$stdout)
sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout)
req = HTTPRequest.new(@config)
res = HTTPResponse.new(@config)
unless @config[:NPH] or defined?(MOD_RUBY)
def res.setup_header
unless @header["status"]
phrase = HTTPStatus::reason_phrase(@status)
@header["status"] = "#{@status} #{phrase}"
end
super
end
def res.status_line
""
end
end
begin
req.parse(sock)
req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup
req.path_info = (env["PATH_INFO"] || "").dup
req.query_string = env["QUERY_STRING"]
req.user = env["REMOTE_USER"]
res.request_method = req.request_method
res.request_uri = req.request_uri
res.request_http_version = req.http_version
res.keep_alive = req.keep_alive?
self.service(req, res)
rescue HTTPStatus::Error => ex
res.set_error(ex)
rescue HTTPStatus::Status => ex
res.status = ex.code
rescue Exception => ex
@logger.error(ex)
res.set_error(ex, true)
ensure
req.fixup
if defined?(MOD_RUBY)
res.setup_header
Apache.request.status_line = "#{res.status} #{res.reason_phrase}"
Apache.request.status = res.status
table = Apache.request.headers_out
res.header.each{|key, val|
case key
when /^content-encoding$/i
Apache::request.content_encoding = val
when /^content-type$/i
Apache::request.content_type = val
else
table[key] = val.to_s
end
}
res.cookies.each{|cookie|
table.add("Set-Cookie", cookie.to_s)
}
Apache.request.send_http_header
res.send_body(sock)
else
res.send_response(sock)
end
end
end
##
# Services the request +req+ which will fill in the response +res+. See
# WEBrick::HTTPServlet::AbstractServlet#service for details.
def service(req, res)
method_name = "do_" + req.request_method.gsub(/-/, "_")
if respond_to?(method_name)
__send__(method_name, req, res)
else
raise HTTPStatus::MethodNotAllowed,
"unsupported method `#{req.request_method}'."
end
end
##
# Provides HTTP socket emulation from the CGI environment
class Socket # :nodoc:
include Enumerable
private
def initialize(config, env, stdin, stdout)
@config = config
@env = env
@header_part = StringIO.new
@body_part = stdin
@out_port = stdout
@out_port.binmode
@server_addr = @env["SERVER_ADDR"] || "0.0.0.0"
@server_name = @env["SERVER_NAME"]
@server_port = @env["SERVER_PORT"]
@remote_addr = @env["REMOTE_ADDR"]
@remote_host = @env["REMOTE_HOST"] || @remote_addr
@remote_port = @env["REMOTE_PORT"] || 0
begin
@header_part << request_line << CRLF
setup_header
@header_part << CRLF
@header_part.rewind
rescue Exception
raise CGIError, "invalid CGI environment"
end
end
def request_line
meth = @env["REQUEST_METHOD"] || "GET"
unless url = @env["REQUEST_URI"]
url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup
url << @env["PATH_INFO"].to_s
url = WEBrick::HTTPUtils.escape_path(url)
if query_string = @env["QUERY_STRING"]
unless query_string.empty?
url << "?" << query_string
end
end
end
# we cannot get real HTTP version of client ;)
httpv = @config[:HTTPVersion]
return "#{meth} #{url} HTTP/#{httpv}"
end
def setup_header
@env.each{|key, value|
case key
when "CONTENT_TYPE", "CONTENT_LENGTH"
add_header(key.gsub(/_/, "-"), value)
when /^HTTP_(.*)/
add_header($1.gsub(/_/, "-"), value)
end
}
end
def add_header(hdrname, value)
unless value.empty?
@header_part << hdrname << ": " << value << CRLF
end
end
def input
@header_part.eof? ? @body_part : @header_part
end
public
def peeraddr
[nil, @remote_port, @remote_host, @remote_addr]
end
def addr
[nil, @server_port, @server_name, @server_addr]
end
def gets(eol=LF, size=nil)
input.gets(eol, size)
end
def read(size=nil)
input.read(size)
end
def each
input.each{|line| yield(line) }
end
def eof?
input.eof?
end
def <<(data)
@out_port << data
end
def write(data)
@out_port.write(data)
end
def cert
return nil unless defined?(OpenSSL)
if pem = @env["SSL_SERVER_CERT"]
OpenSSL::X509::Certificate.new(pem) unless pem.empty?
end
end
def peer_cert
return nil unless defined?(OpenSSL)
if pem = @env["SSL_CLIENT_CERT"]
OpenSSL::X509::Certificate.new(pem) unless pem.empty?
end
end
def peer_cert_chain
return nil unless defined?(OpenSSL)
if @env["SSL_CLIENT_CERT_CHAIN_0"]
keys = @env.keys
certs = keys.sort.collect{|k|
if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k
if pem = @env[k]
OpenSSL::X509::Certificate.new(pem) unless pem.empty?
end
end
}
certs.compact
end
end
def cipher
return nil unless defined?(OpenSSL)
if cipher = @env["SSL_CIPHER"]
ret = [ cipher ]
ret << @env["SSL_PROTOCOL"]
ret << @env["SSL_CIPHER_USEKEYSIZE"]
ret << @env["SSL_CIPHER_ALGKEYSIZE"]
ret
end
end
end
end
end

View File

@ -1,36 +0,0 @@
# frozen_string_literal: true
#
# compat.rb -- cross platform compatibility
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2002 GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: compat.rb,v 1.6 2002/10/01 17:16:32 gotoyuzo Exp $
##
# System call error module used by webrick for cross platform compatibility.
#
# EPROTO:: protocol error
# ECONNRESET:: remote host reset the connection request
# ECONNABORTED:: Client sent TCP reset (RST) before server has accepted the
# connection requested by client.
#
module Errno
##
# Protocol error.
class EPROTO < SystemCallError; end
##
# Remote host reset the connection request.
class ECONNRESET < SystemCallError; end
##
# Client sent TCP reset (RST) before server has accepted the connection
# requested by client.
class ECONNABORTED < SystemCallError; end
end

View File

@ -1,158 +0,0 @@
# frozen_string_literal: true
#
# config.rb -- Default configurations.
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $
require_relative 'version'
require_relative 'httpversion'
require_relative 'httputils'
require_relative 'utils'
require_relative 'log'
module WEBrick
module Config
LIBDIR = File::dirname(__FILE__) # :nodoc:
# for GenericServer
General = Hash.new { |hash, key|
case key
when :ServerName
hash[key] = Utils.getservername
else
nil
end
}.update(
:BindAddress => nil, # "0.0.0.0" or "::" or nil
:Port => nil, # users MUST specify this!!
:MaxClients => 100, # maximum number of the concurrent connections
:ServerType => nil, # default: WEBrick::SimpleServer
:Logger => nil, # default: WEBrick::Log.new
:ServerSoftware => "WEBrick/#{WEBrick::VERSION} " +
"(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})",
:TempDir => ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp',
:DoNotListen => false,
:StartCallback => nil,
:StopCallback => nil,
:AcceptCallback => nil,
:DoNotReverseLookup => true,
:ShutdownSocketWithoutClose => false,
)
# for HTTPServer, HTTPRequest, HTTPResponse ...
HTTP = General.dup.update(
:Port => 80,
:RequestTimeout => 30,
:HTTPVersion => HTTPVersion.new("1.1"),
:AccessLog => nil,
:MimeTypes => HTTPUtils::DefaultMimeTypes,
:DirectoryIndex => ["index.html","index.htm","index.cgi","index.rhtml"],
:DocumentRoot => nil,
:DocumentRootOptions => { :FancyIndexing => true },
:RequestCallback => nil,
:ServerAlias => nil,
:InputBufferSize => 65536, # input buffer size in reading request body
:OutputBufferSize => 65536, # output buffer size in sending File or IO
# for HTTPProxyServer
:ProxyAuthProc => nil,
:ProxyContentHandler => nil,
:ProxyVia => true,
:ProxyTimeout => true,
:ProxyURI => nil,
:CGIInterpreter => nil,
:CGIPathEnv => nil,
# workaround: if Request-URIs contain 8bit chars,
# they should be escaped before calling of URI::parse().
:Escape8bitURI => false
)
##
# Default configuration for WEBrick::HTTPServlet::FileHandler
#
# :AcceptableLanguages::
# Array of languages allowed for accept-language. There is no default
# :DirectoryCallback::
# Allows preprocessing of directory requests. There is no default
# callback.
# :FancyIndexing::
# If true, show an index for directories. The default is true.
# :FileCallback::
# Allows preprocessing of file requests. There is no default callback.
# :HandlerCallback::
# Allows preprocessing of requests. There is no default callback.
# :HandlerTable::
# Maps file suffixes to file handlers. DefaultFileHandler is used by
# default but any servlet can be used.
# :NondisclosureName::
# Do not show files matching this array of globs. .ht* and *~ are
# excluded by default.
# :UserDir::
# Directory inside ~user to serve content from for /~user requests.
# Only works if mounted on /. Disabled by default.
FileHandler = {
:NondisclosureName => [".ht*", "*~"],
:FancyIndexing => false,
:HandlerTable => {},
:HandlerCallback => nil,
:DirectoryCallback => nil,
:FileCallback => nil,
:UserDir => nil, # e.g. "public_html"
:AcceptableLanguages => [] # ["en", "ja", ... ]
}
##
# Default configuration for WEBrick::HTTPAuth::BasicAuth
#
# :AutoReloadUserDB:: Reload the user database provided by :UserDB
# automatically?
BasicAuth = {
:AutoReloadUserDB => true,
}
##
# Default configuration for WEBrick::HTTPAuth::DigestAuth.
#
# :Algorithm:: MD5, MD5-sess (default), SHA1, SHA1-sess
# :Domain:: An Array of URIs that define the protected space
# :Qop:: 'auth' for authentication, 'auth-int' for integrity protection or
# both
# :UseOpaque:: Should the server send opaque values to the client? This
# helps prevent replay attacks.
# :CheckNc:: Should the server check the nonce count? This helps the
# server detect replay attacks.
# :UseAuthenticationInfoHeader:: Should the server send an
# AuthenticationInfo header?
# :AutoReloadUserDB:: Reload the user database provided by :UserDB
# automatically?
# :NonceExpirePeriod:: How long should we store used nonces? Default is
# 30 minutes.
# :NonceExpireDelta:: How long is a nonce valid? Default is 1 minute
# :InternetExplorerHack:: Hack which allows Internet Explorer to work.
# :OperaHack:: Hack which allows Opera to work.
DigestAuth = {
:Algorithm => 'MD5-sess', # or 'MD5'
:Domain => nil, # an array includes domain names.
:Qop => [ 'auth' ], # 'auth' or 'auth-int' or both.
:UseOpaque => true,
:UseNextNonce => false,
:CheckNc => false,
:UseAuthenticationInfoHeader => true,
:AutoReloadUserDB => true,
:NonceExpirePeriod => 30*60,
:NonceExpireDelta => 60,
:InternetExplorerHack => true,
:OperaHack => true,
}
end
end

View File

@ -1,172 +0,0 @@
# frozen_string_literal: true
#
# cookie.rb -- Cookie class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
require 'time'
require_relative 'httputils'
module WEBrick
##
# Processes HTTP cookies
class Cookie
##
# The cookie name
attr_reader :name
##
# The cookie value
attr_accessor :value
##
# The cookie version
attr_accessor :version
##
# The cookie domain
attr_accessor :domain
##
# The cookie path
attr_accessor :path
##
# Is this a secure cookie?
attr_accessor :secure
##
# The cookie comment
attr_accessor :comment
##
# The maximum age of the cookie
attr_accessor :max_age
#attr_accessor :comment_url, :discard, :port
##
# Creates a new cookie with the given +name+ and +value+
def initialize(name, value)
@name = name
@value = value
@version = 0 # Netscape Cookie
@domain = @path = @secure = @comment = @max_age =
@expires = @comment_url = @discard = @port = nil
end
##
# Sets the cookie expiration to the time +t+. The expiration time may be
# a false value to disable expiration or a Time or HTTP format time string
# to set the expiration date.
def expires=(t)
@expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
end
##
# Retrieves the expiration time as a Time
def expires
@expires && Time.parse(@expires)
end
##
# The cookie string suitable for use in an HTTP header
def to_s
ret = +""
ret << @name << "=" << @value
ret << "; " << "Version=" << @version.to_s if @version > 0
ret << "; " << "Domain=" << @domain if @domain
ret << "; " << "Expires=" << @expires if @expires
ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
ret << "; " << "Comment=" << @comment if @comment
ret << "; " << "Path=" << @path if @path
ret << "; " << "Secure" if @secure
ret
end
##
# Parses a Cookie field sent from the user-agent. Returns an array of
# cookies.
def self.parse(str)
if str
ret = []
cookie = nil
ver = 0
str.split(/;\s+/).each{|x|
key, val = x.split(/=/,2)
val = val ? HTTPUtils::dequote(val) : ""
case key
when "$Version"; ver = val.to_i
when "$Path"; cookie.path = val
when "$Domain"; cookie.domain = val
when "$Port"; cookie.port = val
else
ret << cookie if cookie
cookie = self.new(key, val)
cookie.version = ver
end
}
ret << cookie if cookie
ret
end
end
##
# Parses the cookie in +str+
def self.parse_set_cookie(str)
cookie_elem = str.split(/;/)
first_elem = cookie_elem.shift
first_elem.strip!
key, value = first_elem.split(/=/, 2)
cookie = new(key, HTTPUtils.dequote(value))
cookie_elem.each{|pair|
pair.strip!
key, value = pair.split(/=/, 2)
if value
value = HTTPUtils.dequote(value.strip)
end
case key.downcase
when "domain" then cookie.domain = value
when "path" then cookie.path = value
when "expires" then cookie.expires = value
when "max-age" then cookie.max_age = Integer(value)
when "comment" then cookie.comment = value
when "version" then cookie.version = Integer(value)
when "secure" then cookie.secure = true
end
}
return cookie
end
##
# Parses the cookies in +str+
def self.parse_set_cookies(str)
return str.split(/,(?=[^;,]*=)|,$/).collect{|c|
parse_set_cookie(c)
}
end
end
end

View File

@ -1,30 +0,0 @@
# frozen_string_literal: true
#--
# htmlutils.rb -- HTMLUtils Module
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: htmlutils.rb,v 1.7 2002/09/21 12:23:35 gotoyuzo Exp $
module WEBrick
module HTMLUtils
##
# Escapes &, ", > and < in +string+
def escape(string)
return "" unless string
str = string.b
str.gsub!(/&/n, '&amp;')
str.gsub!(/\"/n, '&quot;')
str.gsub!(/>/n, '&gt;')
str.gsub!(/</n, '&lt;')
str.force_encoding(string.encoding)
end
module_function :escape
end
end

View File

@ -1,96 +0,0 @@
# frozen_string_literal: true
#
# httpauth.rb -- HTTP access authentication
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
require_relative 'httpauth/basicauth'
require_relative 'httpauth/digestauth'
require_relative 'httpauth/htpasswd'
require_relative 'httpauth/htdigest'
require_relative 'httpauth/htgroup'
module WEBrick
##
# HTTPAuth provides both basic and digest authentication.
#
# To enable authentication for requests in WEBrick you will need a user
# database and an authenticator. To start, here's an Htpasswd database for
# use with a DigestAuth authenticator:
#
# config = { :Realm => 'DigestAuth example realm' }
#
# htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
# htpasswd.auth_type = WEBrick::HTTPAuth::DigestAuth
# htpasswd.set_passwd config[:Realm], 'username', 'password'
# htpasswd.flush
#
# The +:Realm+ is used to provide different access to different groups
# across several resources on a server. Typically you'll need only one
# realm for a server.
#
# This database can be used to create an authenticator:
#
# config[:UserDB] = htpasswd
#
# digest_auth = WEBrick::HTTPAuth::DigestAuth.new config
#
# To authenticate a request call #authenticate with a request and response
# object in a servlet:
#
# def do_GET req, res
# @authenticator.authenticate req, res
# end
#
# For digest authentication the authenticator must not be created every
# request, it must be passed in as an option via WEBrick::HTTPServer#mount.
module HTTPAuth
module_function
def _basic_auth(req, res, realm, req_field, res_field, err_type,
block) # :nodoc:
user = pass = nil
if /^Basic\s+(.*)/o =~ req[req_field]
userpass = $1
user, pass = userpass.unpack("m*")[0].split(":", 2)
end
if block.call(user, pass)
req.user = user
return
end
res[res_field] = "Basic realm=\"#{realm}\""
raise err_type
end
##
# Simple wrapper for providing basic authentication for a request. When
# called with a request +req+, response +res+, authentication +realm+ and
# +block+ the block will be called with a +username+ and +password+. If
# the block returns true the request is allowed to continue, otherwise an
# HTTPStatus::Unauthorized error is raised.
def basic_auth(req, res, realm, &block) # :yield: username, password
_basic_auth(req, res, realm, "Authorization", "WWW-Authenticate",
HTTPStatus::Unauthorized, block)
end
##
# Simple wrapper for providing basic authentication for a proxied request.
# When called with a request +req+, response +res+, authentication +realm+
# and +block+ the block will be called with a +username+ and +password+.
# If the block returns true the request is allowed to continue, otherwise
# an HTTPStatus::ProxyAuthenticationRequired error is raised.
def proxy_basic_auth(req, res, realm, &block) # :yield: username, password
_basic_auth(req, res, realm, "Proxy-Authorization", "Proxy-Authenticate",
HTTPStatus::ProxyAuthenticationRequired, block)
end
end
end

View File

@ -1,117 +0,0 @@
# frozen_string_literal: true
#--
# httpauth/authenticator.rb -- Authenticator mix-in module.
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: authenticator.rb,v 1.3 2003/02/20 07:15:47 gotoyuzo Exp $
module WEBrick
module HTTPAuth
##
# Module providing generic support for both Digest and Basic
# authentication schemes.
module Authenticator
RequestField = "Authorization" # :nodoc:
ResponseField = "WWW-Authenticate" # :nodoc:
ResponseInfoField = "Authentication-Info" # :nodoc:
AuthException = HTTPStatus::Unauthorized # :nodoc:
##
# Method of authentication, must be overridden by the including class
AuthScheme = nil
##
# The realm this authenticator covers
attr_reader :realm
##
# The user database for this authenticator
attr_reader :userdb
##
# The logger for this authenticator
attr_reader :logger
private
# :stopdoc:
##
# Initializes the authenticator from +config+
def check_init(config)
[:UserDB, :Realm].each{|sym|
unless config[sym]
raise ArgumentError, "Argument #{sym.inspect} missing."
end
}
@realm = config[:Realm]
@userdb = config[:UserDB]
@logger = config[:Logger] || Log::new($stderr)
@reload_db = config[:AutoReloadUserDB]
@request_field = self::class::RequestField
@response_field = self::class::ResponseField
@resp_info_field = self::class::ResponseInfoField
@auth_exception = self::class::AuthException
@auth_scheme = self::class::AuthScheme
end
##
# Ensures +req+ has credentials that can be authenticated.
def check_scheme(req)
unless credentials = req[@request_field]
error("no credentials in the request.")
return nil
end
unless match = /^#{@auth_scheme}\s+/i.match(credentials)
error("invalid scheme in %s.", credentials)
info("%s: %s", @request_field, credentials) if $DEBUG
return nil
end
return match.post_match
end
def log(meth, fmt, *args)
msg = format("%s %s: ", @auth_scheme, @realm)
msg << fmt % args
@logger.__send__(meth, msg)
end
def error(fmt, *args)
if @logger.error?
log(:error, fmt, *args)
end
end
def info(fmt, *args)
if @logger.info?
log(:info, fmt, *args)
end
end
# :startdoc:
end
##
# Module providing generic support for both Digest and Basic
# authentication schemes for proxies.
module ProxyAuthenticator
RequestField = "Proxy-Authorization" # :nodoc:
ResponseField = "Proxy-Authenticate" # :nodoc:
InfoField = "Proxy-Authentication-Info" # :nodoc:
AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc:
end
end
end

View File

@ -1,116 +0,0 @@
# frozen_string_literal: true
#
# httpauth/basicauth.rb -- HTTP basic access authentication
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
require_relative '../config'
require_relative '../httpstatus'
require_relative 'authenticator'
module WEBrick
module HTTPAuth
##
# Basic Authentication for WEBrick
#
# Use this class to add basic authentication to a WEBrick servlet.
#
# Here is an example of how to set up a BasicAuth:
#
# config = { :Realm => 'BasicAuth example realm' }
#
# htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
# htpasswd.set_passwd config[:Realm], 'username', 'password'
# htpasswd.flush
#
# config[:UserDB] = htpasswd
#
# basic_auth = WEBrick::HTTPAuth::BasicAuth.new config
class BasicAuth
include Authenticator
AuthScheme = "Basic" # :nodoc:
##
# Used by UserDB to create a basic password entry
def self.make_passwd(realm, user, pass)
pass ||= ""
pass.crypt(Utils::random_string(2))
end
attr_reader :realm, :userdb, :logger
##
# Creates a new BasicAuth instance.
#
# See WEBrick::Config::BasicAuth for default configuration entries
#
# You must supply the following configuration entries:
#
# :Realm:: The name of the realm being protected.
# :UserDB:: A database of usernames and passwords.
# A WEBrick::HTTPAuth::Htpasswd instance should be used.
def initialize(config, default=Config::BasicAuth)
check_init(config)
@config = default.dup.update(config)
end
##
# Authenticates a +req+ and returns a 401 Unauthorized using +res+ if
# the authentication was not correct.
def authenticate(req, res)
unless basic_credentials = check_scheme(req)
challenge(req, res)
end
userid, password = basic_credentials.unpack("m*")[0].split(":", 2)
password ||= ""
if userid.empty?
error("user id was not given.")
challenge(req, res)
end
unless encpass = @userdb.get_passwd(@realm, userid, @reload_db)
error("%s: the user is not allowed.", userid)
challenge(req, res)
end
case encpass
when /\A\$2[aby]\$/
password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
else
password_matches = password.crypt(encpass) == encpass
end
unless password_matches
error("%s: password unmatch.", userid)
challenge(req, res)
end
info("%s: authentication succeeded.", userid)
req.user = userid
end
##
# Returns a challenge response which asks for authentication information
def challenge(req, res)
res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
raise @auth_exception
end
end
##
# Basic authentication for proxy servers. See BasicAuth for details.
class ProxyBasicAuth < BasicAuth
include ProxyAuthenticator
end
end
end

View File

@ -1,395 +0,0 @@
# frozen_string_literal: true
#
# httpauth/digestauth.rb -- HTTP digest access authentication
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers.
# Copyright (c) 2003 H.M.
#
# The original implementation is provided by H.M.
# URL: http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb?cmd=view;name=
# %C7%A7%BE%DA%B5%A1%C7%BD%A4%F2%B2%FE%C2%A4%A4%B7%A4%C6%A4%DF%A4%EB
#
# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
require_relative '../config'
require_relative '../httpstatus'
require_relative 'authenticator'
require 'digest/md5'
require 'digest/sha1'
module WEBrick
module HTTPAuth
##
# RFC 2617 Digest Access Authentication for WEBrick
#
# Use this class to add digest authentication to a WEBrick servlet.
#
# Here is an example of how to set up DigestAuth:
#
# config = { :Realm => 'DigestAuth example realm' }
#
# htdigest = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
# htdigest.set_passwd config[:Realm], 'username', 'password'
# htdigest.flush
#
# config[:UserDB] = htdigest
#
# digest_auth = WEBrick::HTTPAuth::DigestAuth.new config
#
# When using this as with a servlet be sure not to create a new DigestAuth
# object in the servlet's #initialize. By default WEBrick creates a new
# servlet instance for every request and the DigestAuth object must be
# used across requests.
class DigestAuth
include Authenticator
AuthScheme = "Digest" # :nodoc:
##
# Struct containing the opaque portion of the digest authentication
OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
##
# Digest authentication algorithm
attr_reader :algorithm
##
# Quality of protection. RFC 2617 defines "auth" and "auth-int"
attr_reader :qop
##
# Used by UserDB to create a digest password entry
def self.make_passwd(realm, user, pass)
pass ||= ""
Digest::MD5::hexdigest([user, realm, pass].join(":"))
end
##
# Creates a new DigestAuth instance. Be sure to use the same DigestAuth
# instance for multiple requests as it saves state between requests in
# order to perform authentication.
#
# See WEBrick::Config::DigestAuth for default configuration entries
#
# You must supply the following configuration entries:
#
# :Realm:: The name of the realm being protected.
# :UserDB:: A database of usernames and passwords.
# A WEBrick::HTTPAuth::Htdigest instance should be used.
def initialize(config, default=Config::DigestAuth)
check_init(config)
@config = default.dup.update(config)
@algorithm = @config[:Algorithm]
@domain = @config[:Domain]
@qop = @config[:Qop]
@use_opaque = @config[:UseOpaque]
@use_next_nonce = @config[:UseNextNonce]
@check_nc = @config[:CheckNc]
@use_auth_info_header = @config[:UseAuthenticationInfoHeader]
@nonce_expire_period = @config[:NonceExpirePeriod]
@nonce_expire_delta = @config[:NonceExpireDelta]
@internet_explorer_hack = @config[:InternetExplorerHack]
case @algorithm
when 'MD5','MD5-sess'
@h = Digest::MD5
when 'SHA1','SHA1-sess' # it is a bonus feature :-)
@h = Digest::SHA1
else
msg = format('Algorithm "%s" is not supported.', @algorithm)
raise ArgumentError.new(msg)
end
@instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
@opaques = {}
@last_nonce_expire = Time.now
@mutex = Thread::Mutex.new
end
##
# Authenticates a +req+ and returns a 401 Unauthorized using +res+ if
# the authentication was not correct.
def authenticate(req, res)
unless result = @mutex.synchronize{ _authenticate(req, res) }
challenge(req, res)
end
if result == :nonce_is_stale
challenge(req, res, true)
end
return true
end
##
# Returns a challenge response which asks for authentication information
def challenge(req, res, stale=false)
nonce = generate_next_nonce(req)
if @use_opaque
opaque = generate_opaque(req)
@opaques[opaque].nonce = nonce
end
param = Hash.new
param["realm"] = HTTPUtils::quote(@realm)
param["domain"] = HTTPUtils::quote(@domain.to_a.join(" ")) if @domain
param["nonce"] = HTTPUtils::quote(nonce)
param["opaque"] = HTTPUtils::quote(opaque) if opaque
param["stale"] = stale.to_s
param["algorithm"] = @algorithm
param["qop"] = HTTPUtils::quote(@qop.to_a.join(",")) if @qop
res[@response_field] =
"#{@auth_scheme} " + param.map{|k,v| "#{k}=#{v}" }.join(", ")
info("%s: %s", @response_field, res[@response_field]) if $DEBUG
raise @auth_exception
end
private
# :stopdoc:
MustParams = ['username','realm','nonce','uri','response']
MustParamsAuth = ['cnonce','nc']
def _authenticate(req, res)
unless digest_credentials = check_scheme(req)
return false
end
auth_req = split_param_value(digest_credentials)
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
req_params = MustParams + MustParamsAuth
else
req_params = MustParams
end
req_params.each{|key|
unless auth_req.has_key?(key)
error('%s: parameter missing. "%s"', auth_req['username'], key)
raise HTTPStatus::BadRequest
end
}
if !check_uri(req, auth_req)
raise HTTPStatus::BadRequest
end
if auth_req['realm'] != @realm
error('%s: realm unmatch. "%s" for "%s"',
auth_req['username'], auth_req['realm'], @realm)
return false
end
auth_req['algorithm'] ||= 'MD5'
if auth_req['algorithm'].upcase != @algorithm.upcase
error('%s: algorithm unmatch. "%s" for "%s"',
auth_req['username'], auth_req['algorithm'], @algorithm)
return false
end
if (@qop.nil? && auth_req.has_key?('qop')) ||
(@qop && (! @qop.member?(auth_req['qop'])))
error('%s: the qop is not allowed. "%s"',
auth_req['username'], auth_req['qop'])
return false
end
password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
unless password
error('%s: the user is not allowed.', auth_req['username'])
return false
end
nonce_is_invalid = false
if @use_opaque
info("@opaque = %s", @opaque.inspect) if $DEBUG
if !(opaque = auth_req['opaque'])
error('%s: opaque is not given.', auth_req['username'])
nonce_is_invalid = true
elsif !(opaque_struct = @opaques[opaque])
error('%s: invalid opaque is given.', auth_req['username'])
nonce_is_invalid = true
elsif !check_opaque(opaque_struct, req, auth_req)
@opaques.delete(auth_req['opaque'])
nonce_is_invalid = true
end
elsif !check_nonce(req, auth_req)
nonce_is_invalid = true
end
if /-sess$/i =~ auth_req['algorithm']
ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce'])
else
ha1 = password
end
if auth_req['qop'] == "auth" || auth_req['qop'] == nil
ha2 = hexdigest(req.request_method, auth_req['uri'])
ha2_res = hexdigest("", auth_req['uri'])
elsif auth_req['qop'] == "auth-int"
body_digest = @h.new
req.body { |chunk| body_digest.update(chunk) }
body_digest = body_digest.hexdigest
ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
ha2_res = hexdigest("", auth_req['uri'], body_digest)
end
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
param2 = ['nonce', 'nc', 'cnonce', 'qop'].map{|key|
auth_req[key]
}.join(':')
digest = hexdigest(ha1, param2, ha2)
digest_res = hexdigest(ha1, param2, ha2_res)
else
digest = hexdigest(ha1, auth_req['nonce'], ha2)
digest_res = hexdigest(ha1, auth_req['nonce'], ha2_res)
end
if digest != auth_req['response']
error("%s: digest unmatch.", auth_req['username'])
return false
elsif nonce_is_invalid
error('%s: digest is valid, but nonce is not valid.',
auth_req['username'])
return :nonce_is_stale
elsif @use_auth_info_header
auth_info = {
'nextnonce' => generate_next_nonce(req),
'rspauth' => digest_res
}
if @use_opaque
opaque_struct.time = req.request_time
opaque_struct.nonce = auth_info['nextnonce']
opaque_struct.nc = "%08x" % (auth_req['nc'].hex + 1)
end
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
['qop','cnonce','nc'].each{|key|
auth_info[key] = auth_req[key]
}
end
res[@resp_info_field] = auth_info.keys.map{|key|
if key == 'nc'
key + '=' + auth_info[key]
else
key + "=" + HTTPUtils::quote(auth_info[key])
end
}.join(', ')
end
info('%s: authentication succeeded.', auth_req['username'])
req.user = auth_req['username']
return true
end
def split_param_value(string)
ret = {}
string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
end
ret
end
def generate_next_nonce(req)
now = "%012d" % req.request_time.to_i
pk = hexdigest(now, @instance_key)[0,32]
nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars.
nonce
end
def check_nonce(req, auth_req)
username = auth_req['username']
nonce = auth_req['nonce']
pub_time, pk = nonce.unpack("m*")[0].split(":", 2)
if (!pub_time || !pk)
error("%s: empty nonce is given", username)
return false
elsif (hexdigest(pub_time, @instance_key)[0,32] != pk)
error("%s: invalid private-key: %s for %s",
username, hexdigest(pub_time, @instance_key)[0,32], pk)
return false
end
diff_time = req.request_time.to_i - pub_time.to_i
if (diff_time < 0)
error("%s: difference of time-stamp is negative.", username)
return false
elsif diff_time > @nonce_expire_period
error("%s: nonce is expired.", username)
return false
end
return true
end
def generate_opaque(req)
@mutex.synchronize{
now = req.request_time
if now - @last_nonce_expire > @nonce_expire_delta
@opaques.delete_if{|key,val|
(now - val.time) > @nonce_expire_period
}
@last_nonce_expire = now
end
begin
opaque = Utils::random_string(16)
end while @opaques[opaque]
@opaques[opaque] = OpaqueInfo.new(now, nil, '00000001')
opaque
}
end
def check_opaque(opaque_struct, req, auth_req)
if (@use_next_nonce && auth_req['nonce'] != opaque_struct.nonce)
error('%s: nonce unmatched. "%s" for "%s"',
auth_req['username'], auth_req['nonce'], opaque_struct.nonce)
return false
elsif !check_nonce(req, auth_req)
return false
end
if (@check_nc && auth_req['nc'] != opaque_struct.nc)
error('%s: nc unmatched."%s" for "%s"',
auth_req['username'], auth_req['nc'], opaque_struct.nc)
return false
end
true
end
def check_uri(req, auth_req)
uri = auth_req['uri']
if uri != req.request_uri.to_s && uri != req.unparsed_uri &&
(@internet_explorer_hack && uri != req.path)
error('%s: uri unmatch. "%s" for "%s"', auth_req['username'],
auth_req['uri'], req.request_uri.to_s)
return false
end
true
end
def hexdigest(*args)
@h.hexdigest(args.join(":"))
end
# :startdoc:
end
##
# Digest authentication for proxy servers. See DigestAuth for details.
class ProxyDigestAuth < DigestAuth
include ProxyAuthenticator
private
def check_uri(req, auth_req) # :nodoc:
return true
end
end
end
end

View File

@ -1,132 +0,0 @@
# frozen_string_literal: true
#
# httpauth/htdigest.rb -- Apache compatible htdigest file
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
require_relative 'userdb'
require_relative 'digestauth'
require 'tempfile'
module WEBrick
module HTTPAuth
##
# Htdigest accesses apache-compatible digest password files. Passwords are
# matched to a realm where they are valid. For security, the path for a
# digest password database should be stored outside of the paths available
# to the HTTP server.
#
# Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and
# stores passwords using cryptographic hashes.
#
# htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
# htpasswd.set_passwd 'my realm', 'username', 'password'
# htpasswd.flush
class Htdigest
include UserDB
##
# Open a digest password database at +path+
def initialize(path)
@path = path
@mtime = Time.at(0)
@digest = Hash.new
@mutex = Thread::Mutex::new
@auth_type = DigestAuth
File.open(@path,"a").close unless File.exist?(@path)
reload
end
##
# Reloads passwords from the database
def reload
mtime = File::mtime(@path)
if mtime > @mtime
@digest.clear
File.open(@path){|io|
while line = io.gets
line.chomp!
user, realm, pass = line.split(/:/, 3)
unless @digest[realm]
@digest[realm] = Hash.new
end
@digest[realm][user] = pass
end
}
@mtime = mtime
end
end
##
# Flush the password database. If +output+ is given the database will
# be written there instead of to the original path.
def flush(output=nil)
output ||= @path
tmp = Tempfile.create("htpasswd", File::dirname(output))
renamed = false
begin
each{|item| tmp.puts(item.join(":")) }
tmp.close
File::rename(tmp.path, output)
renamed = true
ensure
tmp.close
File.unlink(tmp.path) if !renamed
end
end
##
# Retrieves a password from the database for +user+ in +realm+. If
# +reload_db+ is true the database will be reloaded first.
def get_passwd(realm, user, reload_db)
reload() if reload_db
if hash = @digest[realm]
hash[user]
end
end
##
# Sets a password in the database for +user+ in +realm+ to +pass+.
def set_passwd(realm, user, pass)
@mutex.synchronize{
unless @digest[realm]
@digest[realm] = Hash.new
end
@digest[realm][user] = make_passwd(realm, user, pass)
}
end
##
# Removes a password from the database for +user+ in +realm+.
def delete_passwd(realm, user)
if hash = @digest[realm]
hash.delete(user)
end
end
##
# Iterate passwords in the database.
def each # :yields: [user, realm, password_hash]
@digest.keys.sort.each{|realm|
hash = @digest[realm]
hash.keys.sort.each{|user|
yield([user, realm, hash[user]])
}
}
end
end
end
end

View File

@ -1,97 +0,0 @@
# frozen_string_literal: true
#
# httpauth/htgroup.rb -- Apache compatible htgroup file
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $
require 'tempfile'
module WEBrick
module HTTPAuth
##
# Htgroup accesses apache-compatible group files. Htgroup can be used to
# provide group-based authentication for users. Currently Htgroup is not
# directly integrated with any authenticators in WEBrick. For security,
# the path for a digest password database should be stored outside of the
# paths available to the HTTP server.
#
# Example:
#
# htgroup = WEBrick::HTTPAuth::Htgroup.new 'my_group_file'
# htgroup.add 'superheroes', %w[spiderman batman]
#
# htgroup.members('superheroes').include? 'magneto' # => false
class Htgroup
##
# Open a group database at +path+
def initialize(path)
@path = path
@mtime = Time.at(0)
@group = Hash.new
File.open(@path,"a").close unless File.exist?(@path)
reload
end
##
# Reload groups from the database
def reload
if (mtime = File::mtime(@path)) > @mtime
@group.clear
File.open(@path){|io|
while line = io.gets
line.chomp!
group, members = line.split(/:\s*/)
@group[group] = members.split(/\s+/)
end
}
@mtime = mtime
end
end
##
# Flush the group database. If +output+ is given the database will be
# written there instead of to the original path.
def flush(output=nil)
output ||= @path
tmp = Tempfile.create("htgroup", File::dirname(output))
begin
@group.keys.sort.each{|group|
tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
}
ensure
tmp.close
if $!
File.unlink(tmp.path)
else
return File.rename(tmp.path, output)
end
end
end
##
# Retrieve the list of members from +group+
def members(group)
reload
@group[group] || []
end
##
# Add an Array of +members+ to +group+
def add(group, members)
@group[group] = members(group) | members
end
end
end
end

View File

@ -1,158 +0,0 @@
# frozen_string_literal: true
#
# httpauth/htpasswd -- Apache compatible htpasswd file
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
require_relative 'userdb'
require_relative 'basicauth'
require 'tempfile'
module WEBrick
module HTTPAuth
##
# Htpasswd accesses apache-compatible password files. Passwords are
# matched to a realm where they are valid. For security, the path for a
# password database should be stored outside of the paths available to the
# HTTP server.
#
# Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth.
#
# To create an Htpasswd database with a single user:
#
# htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
# htpasswd.set_passwd 'my realm', 'username', 'password'
# htpasswd.flush
class Htpasswd
include UserDB
##
# Open a password database at +path+
def initialize(path, password_hash: nil)
@path = path
@mtime = Time.at(0)
@passwd = Hash.new
@auth_type = BasicAuth
@password_hash = password_hash
case @password_hash
when nil
# begin
# require "string/crypt"
# rescue LoadError
# warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
# end
@password_hash = :crypt
when :crypt
# require "string/crypt"
when :bcrypt
require "bcrypt"
else
raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
end
File.open(@path,"a").close unless File.exist?(@path)
reload
end
##
# Reload passwords from the database
def reload
mtime = File::mtime(@path)
if mtime > @mtime
@passwd.clear
File.open(@path){|io|
while line = io.gets
line.chomp!
case line
when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
if @password_hash == :bcrypt
raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
end
user, pass = line.split(":")
when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
if @password_hash == :crypt
raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
end
user, pass = line.split(":")
when /:\$/, /:{SHA}/
raise NotImplementedError,
'MD5, SHA1 .htpasswd file not supported'
else
raise StandardError, 'bad .htpasswd file'
end
@passwd[user] = pass
end
}
@mtime = mtime
end
end
##
# Flush the password database. If +output+ is given the database will
# be written there instead of to the original path.
def flush(output=nil)
output ||= @path
tmp = Tempfile.create("htpasswd", File::dirname(output))
renamed = false
begin
each{|item| tmp.puts(item.join(":")) }
tmp.close
File::rename(tmp.path, output)
renamed = true
ensure
tmp.close
File.unlink(tmp.path) if !renamed
end
end
##
# Retrieves a password from the database for +user+ in +realm+. If
# +reload_db+ is true the database will be reloaded first.
def get_passwd(realm, user, reload_db)
reload() if reload_db
@passwd[user]
end
##
# Sets a password in the database for +user+ in +realm+ to +pass+.
def set_passwd(realm, user, pass)
if @password_hash == :bcrypt
# Cost of 5 to match Apache default, and because the
# bcrypt default of 10 will introduce significant delays
# for every request.
@passwd[user] = BCrypt::Password.create(pass, :cost=>5)
else
@passwd[user] = make_passwd(realm, user, pass)
end
end
##
# Removes a password from the database for +user+ in +realm+.
def delete_passwd(realm, user)
@passwd.delete(user)
end
##
# Iterate passwords in the database.
def each # :yields: [user, password]
@passwd.keys.sort.each{|user|
yield([user, @passwd[user]])
}
end
end
end
end

View File

@ -1,53 +0,0 @@
# frozen_string_literal: true
#--
# httpauth/userdb.rb -- UserDB mix-in module.
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $
module WEBrick
module HTTPAuth
##
# User database mixin for HTTPAuth. This mixin dispatches user record
# access to the underlying auth_type for this database.
module UserDB
##
# The authentication type.
#
# WEBrick::HTTPAuth::BasicAuth or WEBrick::HTTPAuth::DigestAuth are
# built-in.
attr_accessor :auth_type
##
# Creates an obscured password in +realm+ with +user+ and +password+
# using the auth_type of this database.
def make_passwd(realm, user, pass)
@auth_type::make_passwd(realm, user, pass)
end
##
# Sets a password in +realm+ with +user+ and +password+ for the
# auth_type of this database.
def set_passwd(realm, user, pass)
self[user] = pass
end
##
# Retrieves a password in +realm+ for +user+ for the auth_type of this
# database. +reload_db+ is a dummy value.
def get_passwd(realm, user, reload_db=false)
make_passwd(realm, user, self[user])
end
end
end
end

View File

@ -1,354 +0,0 @@
# frozen_string_literal: true
#
# httpproxy.rb -- HTTPProxy Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2002 GOTO Kentaro
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
require_relative "httpserver"
require "net/http"
module WEBrick
NullReader = Object.new # :nodoc:
class << NullReader # :nodoc:
def read(*args)
nil
end
alias gets read
end
FakeProxyURI = Object.new # :nodoc:
class << FakeProxyURI # :nodoc:
def method_missing(meth, *args)
if %w(scheme host port path query userinfo).member?(meth.to_s)
return nil
end
super
end
end
# :startdoc:
##
# An HTTP Proxy server which proxies GET, HEAD and POST requests.
#
# To create a simple proxy server:
#
# require 'webrick'
# require 'webrick/httpproxy'
#
# proxy = WEBrick::HTTPProxyServer.new Port: 8000
#
# trap 'INT' do proxy.shutdown end
# trap 'TERM' do proxy.shutdown end
#
# proxy.start
#
# See ::new for proxy-specific configuration items.
#
# == Modifying proxied responses
#
# To modify content the proxy server returns use the +:ProxyContentHandler+
# option:
#
# handler = proc do |req, res|
# if res['content-type'] == 'text/plain' then
# res.body << "\nThis content was proxied!\n"
# end
# end
#
# proxy =
# WEBrick::HTTPProxyServer.new Port: 8000, ProxyContentHandler: handler
class HTTPProxyServer < HTTPServer
##
# Proxy server configurations. The proxy server handles the following
# configuration items in addition to those supported by HTTPServer:
#
# :ProxyAuthProc:: Called with a request and response to authorize a
# request
# :ProxyVia:: Appended to the via header
# :ProxyURI:: The proxy server's URI
# :ProxyContentHandler:: Called with a request and response and allows
# modification of the response
# :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60
# seconds for read operations
def initialize(config={}, default=Config::HTTP)
super(config, default)
c = @config
@via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
end
# :stopdoc:
def service(req, res)
if req.request_method == "CONNECT"
do_CONNECT(req, res)
elsif req.unparsed_uri =~ %r!^http://!
proxy_service(req, res)
else
super(req, res)
end
end
def proxy_auth(req, res)
if proc = @config[:ProxyAuthProc]
proc.call(req, res)
end
req.header.delete("proxy-authorization")
end
def proxy_uri(req, res)
# should return upstream proxy server's URI
return @config[:ProxyURI]
end
def proxy_service(req, res)
# Proxy Authentication
proxy_auth(req, res)
begin
public_send("do_#{req.request_method}", req, res)
rescue NoMethodError
raise HTTPStatus::MethodNotAllowed,
"unsupported method `#{req.request_method}'."
rescue => err
logger.debug("#{err.class}: #{err.message}")
raise HTTPStatus::ServiceUnavailable, err.message
end
# Process contents
if handler = @config[:ProxyContentHandler]
handler.call(req, res)
end
end
def do_CONNECT(req, res)
# Proxy Authentication
proxy_auth(req, res)
ua = Thread.current[:WEBrickSocket] # User-Agent
raise HTTPStatus::InternalServerError,
"[BUG] cannot get socket" unless ua
host, port = req.unparsed_uri.split(":", 2)
# Proxy authentication for upstream proxy server
if proxy = proxy_uri(req, res)
proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
if proxy.userinfo
credentials = "Basic " + [proxy.userinfo].pack("m0")
end
host, port = proxy.host, proxy.port
end
begin
@logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
os = TCPSocket.new(host, port) # origin server
if proxy
@logger.debug("CONNECT: sending a Request-Line")
os << proxy_request_line << CRLF
@logger.debug("CONNECT: > #{proxy_request_line}")
if credentials
@logger.debug("CONNECT: sending credentials")
os << "Proxy-Authorization: " << credentials << CRLF
end
os << CRLF
proxy_status_line = os.gets(LF)
@logger.debug("CONNECT: read Status-Line from the upstream server")
@logger.debug("CONNECT: < #{proxy_status_line}")
if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
while line = os.gets(LF)
break if /\A(#{CRLF}|#{LF})\z/om =~ line
end
else
raise HTTPStatus::BadGateway
end
end
@logger.debug("CONNECT #{host}:#{port}: succeeded")
res.status = HTTPStatus::RC_OK
rescue => ex
@logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
res.set_error(ex)
raise HTTPStatus::EOFError
ensure
if handler = @config[:ProxyContentHandler]
handler.call(req, res)
end
res.send_response(ua)
access_log(@config, req, res)
# Should clear request-line not to send the response twice.
# see: HTTPServer#run
req.parse(NullReader) rescue nil
end
begin
while fds = IO::select([ua, os])
if fds[0].member?(ua)
buf = ua.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
os.write(buf)
elsif fds[0].member?(os)
buf = os.readpartial(1024);
@logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
ua.write(buf)
end
end
rescue
os.close
@logger.debug("CONNECT #{host}:#{port}: closed")
end
raise HTTPStatus::EOFError
end
def do_GET(req, res)
perform_proxy_request(req, res, Net::HTTP::Get)
end
def do_HEAD(req, res)
perform_proxy_request(req, res, Net::HTTP::Head)
end
def do_POST(req, res)
perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
end
def do_OPTIONS(req, res)
res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
end
private
# Some header fields should not be transferred.
HopByHop = %w( connection keep-alive proxy-authenticate upgrade
proxy-authorization te trailers transfer-encoding )
ShouldNotTransfer = %w( set-cookie proxy-connection )
def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
def choose_header(src, dst)
connections = split_field(src['connection'])
src.each{|key, value|
key = key.downcase
if HopByHop.member?(key) || # RFC2616: 13.5.1
connections.member?(key) || # RFC2616: 14.10
ShouldNotTransfer.member?(key) # pragmatics
@logger.debug("choose_header: `#{key}: #{value}'")
next
end
dst[key] = value
}
end
# Net::HTTP is stupid about the multiple header fields.
# Here is workaround:
def set_cookie(src, dst)
if str = src['set-cookie']
cookies = []
str.split(/,\s*/).each{|token|
if /^[^=]+;/o =~ token
cookies[-1] << ", " << token
elsif /=/o =~ token
cookies << token
else
cookies[-1] << ", " << token
end
}
dst.cookies.replace(cookies)
end
end
def set_via(h)
if @config[:ProxyVia]
if h['via']
h['via'] << ", " << @via
else
h['via'] = @via
end
end
end
def setup_proxy_header(req, res)
# Choose header fields to transfer
header = Hash.new
choose_header(req, header)
set_via(header)
return header
end
def setup_upstream_proxy_authentication(req, res, header)
if upstream = proxy_uri(req, res)
if upstream.userinfo
header['proxy-authorization'] =
"Basic " + [upstream.userinfo].pack("m0")
end
return upstream
end
return FakeProxyURI
end
def create_net_http(uri, upstream)
Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
end
def perform_proxy_request(req, res, req_class, body_stream = nil)
uri = req.request_uri
path = uri.path.dup
path << "?" << uri.query if uri.query
header = setup_proxy_header(req, res)
upstream = setup_upstream_proxy_authentication(req, res, header)
body_tmp = []
http = create_net_http(uri, upstream)
req_fib = Fiber.new do
http.start do
if @config[:ProxyTimeout]
################################## these issues are
http.open_timeout = 30 # secs # necessary (maybe because
http.read_timeout = 60 # secs # Ruby's bug, but why?)
##################################
end
if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
header['Transfer-Encoding'] = 'chunked'
end
http_req = req_class.new(path, header)
http_req.body_stream = body_stream if body_stream
http.request(http_req) do |response|
# Persistent connection requirements are mysterious for me.
# So I will close the connection in every response.
res['proxy-connection'] = "close"
res['connection'] = "close"
# stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
res.status = response.code.to_i
res.chunked = response.chunked?
choose_header(response, res)
set_cookie(response, res)
set_via(res)
response.read_body do |buf|
body_tmp << buf
Fiber.yield # wait for res.body Proc#call
end
end # http.request
end
end
req_fib.resume # read HTTP response headers and first chunk of the body
res.body = ->(socket) do
while buf = body_tmp.shift
socket.write(buf)
buf.clear
req_fib.resume # continue response.read_body
end
end
end
# :stopdoc:
end
end

View File

@ -1,640 +0,0 @@
# frozen_string_literal: true
#
# httprequest.rb -- HTTPRequest Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
require 'fiber'
require 'uri'
require_relative 'httpversion'
require_relative 'httpstatus'
require_relative 'httputils'
require_relative 'cookie'
module WEBrick
##
# An HTTP request. This is consumed by service and do_* methods in
# WEBrick servlets
class HTTPRequest
BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] # :nodoc:
# :section: Request line
##
# The complete request line such as:
#
# GET / HTTP/1.1
attr_reader :request_line
##
# The request method, GET, POST, PUT, etc.
attr_reader :request_method
##
# The unparsed URI of the request
attr_reader :unparsed_uri
##
# The HTTP version of the request
attr_reader :http_version
# :section: Request-URI
##
# The parsed URI of the request
attr_reader :request_uri
##
# The request path
attr_reader :path
##
# The script name (CGI variable)
attr_accessor :script_name
##
# The path info (CGI variable)
attr_accessor :path_info
##
# The query from the URI of the request
attr_accessor :query_string
# :section: Header and entity body
##
# The raw header of the request
attr_reader :raw_header
##
# The parsed header of the request
attr_reader :header
##
# The parsed request cookies
attr_reader :cookies
##
# The Accept header value
attr_reader :accept
##
# The Accept-Charset header value
attr_reader :accept_charset
##
# The Accept-Encoding header value
attr_reader :accept_encoding
##
# The Accept-Language header value
attr_reader :accept_language
# :section:
##
# The remote user (CGI variable)
attr_accessor :user
##
# The socket address of the server
attr_reader :addr
##
# The socket address of the client
attr_reader :peeraddr
##
# Hash of request attributes
attr_reader :attributes
##
# Is this a keep-alive connection?
attr_reader :keep_alive
##
# The local time this request was received
attr_reader :request_time
##
# Creates a new HTTP request. WEBrick::Config::HTTP is the default
# configuration.
def initialize(config)
@config = config
@buffer_size = @config[:InputBufferSize]
@logger = config[:Logger]
@request_line = @request_method =
@unparsed_uri = @http_version = nil
@request_uri = @host = @port = @path = nil
@script_name = @path_info = nil
@query_string = nil
@query = nil
@form_data = nil
@raw_header = Array.new
@header = nil
@cookies = []
@accept = []
@accept_charset = []
@accept_encoding = []
@accept_language = []
@body = +""
@addr = @peeraddr = nil
@attributes = {}
@user = nil
@keep_alive = false
@request_time = nil
@remaining_size = nil
@socket = nil
@forwarded_proto = @forwarded_host = @forwarded_port =
@forwarded_server = @forwarded_for = nil
end
##
# Parses a request from +socket+. This is called internally by
# WEBrick::HTTPServer.
def parse(socket=nil)
@socket = socket
begin
@peeraddr = socket.respond_to?(:peeraddr) ? socket.peeraddr : []
@addr = socket.respond_to?(:addr) ? socket.addr : []
rescue Errno::ENOTCONN
raise HTTPStatus::EOFError
end
read_request_line(socket)
if @http_version.major > 0
read_header(socket)
@header['cookie'].each{|cookie|
@cookies += Cookie::parse(cookie)
}
@accept = HTTPUtils.parse_qvalues(self['accept'])
@accept_charset = HTTPUtils.parse_qvalues(self['accept-charset'])
@accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding'])
@accept_language = HTTPUtils.parse_qvalues(self['accept-language'])
end
return if @request_method == "CONNECT"
return if @unparsed_uri == "*"
begin
setup_forwarded_info
@request_uri = parse_uri(@unparsed_uri)
@path = HTTPUtils::unescape(@request_uri.path)
@path = HTTPUtils::normalize_path(@path)
@host = @request_uri.host
@port = @request_uri.port
@query_string = @request_uri.query
@script_name = ""
@path_info = @path.dup
rescue
raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
end
if /\Aclose\z/io =~ self["connection"]
@keep_alive = false
elsif /\Akeep-alive\z/io =~ self["connection"]
@keep_alive = true
elsif @http_version < "1.1"
@keep_alive = false
else
@keep_alive = true
end
end
##
# Generate HTTP/1.1 100 continue response if the client expects it,
# otherwise does nothing.
def continue # :nodoc:
if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1"
@socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}"
@header.delete('expect')
end
end
##
# Returns the request body.
def body(&block) # :yields: body_chunk
block ||= Proc.new{|chunk| @body << chunk }
read_body(@socket, block)
@body.empty? ? nil : @body
end
##
# Prepares the HTTPRequest object for use as the
# source for IO.copy_stream
def body_reader
@body_tmp = []
@body_rd = Fiber.new do
body do |buf|
@body_tmp << buf
Fiber.yield
end
end
@body_rd.resume # grab the first chunk and yield
self
end
# for IO.copy_stream.
def readpartial(size, buf = ''.b) # :nodoc
res = @body_tmp.shift or raise EOFError, 'end of file reached'
if res.length > size
@body_tmp.unshift(res[size..-1])
res = res[0..size - 1]
end
buf.replace(res)
res.clear
# get more chunks - check alive? because we can take a partial chunk
@body_rd.resume if @body_rd.alive?
buf
end
##
# Request query as a Hash
def query
unless @query
parse_query()
end
@query
end
##
# The content-length header
def content_length
return Integer(self['content-length'])
end
##
# The content-type header
def content_type
return self['content-type']
end
##
# Retrieves +header_name+
def [](header_name)
if @header
value = @header[header_name.downcase]
value.empty? ? nil : value.join(", ")
end
end
##
# Iterates over the request headers
def each
if @header
@header.each{|k, v|
value = @header[k]
yield(k, value.empty? ? nil : value.join(", "))
}
end
end
##
# The host this request is for
def host
return @forwarded_host || @host
end
##
# The port this request is for
def port
return @forwarded_port || @port
end
##
# The server name this request is for
def server_name
return @forwarded_server || @config[:ServerName]
end
##
# The client's IP address
def remote_ip
return self["client-ip"] || @forwarded_for || @peeraddr[3]
end
##
# Is this an SSL request?
def ssl?
return @request_uri.scheme == "https"
end
##
# Should the connection this request was made on be kept alive?
def keep_alive?
@keep_alive
end
def to_s # :nodoc:
ret = @request_line.dup
@raw_header.each{|line| ret << line }
ret << CRLF
ret << body if body
ret
end
##
# Consumes any remaining body and updates keep-alive status
def fixup() # :nodoc:
begin
body{|chunk| } # read remaining body
rescue HTTPStatus::Error => ex
@logger.error("HTTPRequest#fixup: #{ex.class} occurred.")
@keep_alive = false
rescue => ex
@logger.error(ex)
@keep_alive = false
end
end
# This method provides the metavariables defined by the revision 3
# of "The WWW Common Gateway Interface Version 1.1"
# To browse the current document of CGI Version 1.1, see below:
# http://tools.ietf.org/html/rfc3875
def meta_vars
meta = Hash.new
cl = self["Content-Length"]
ct = self["Content-Type"]
meta["CONTENT_LENGTH"] = cl if cl.to_i > 0
meta["CONTENT_TYPE"] = ct.dup if ct
meta["GATEWAY_INTERFACE"] = "CGI/1.1"
meta["PATH_INFO"] = @path_info ? @path_info.dup : ""
#meta["PATH_TRANSLATED"] = nil # no plan to be provided
meta["QUERY_STRING"] = @query_string ? @query_string.dup : ""
meta["REMOTE_ADDR"] = @peeraddr[3]
meta["REMOTE_HOST"] = @peeraddr[2]
#meta["REMOTE_IDENT"] = nil # no plan to be provided
meta["REMOTE_USER"] = @user
meta["REQUEST_METHOD"] = @request_method.dup
meta["REQUEST_URI"] = @request_uri.to_s
meta["SCRIPT_NAME"] = @script_name.dup
meta["SERVER_NAME"] = @host
meta["SERVER_PORT"] = @port.to_s
meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s
meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup
self.each{|key, val|
next if /^content-type$/i =~ key
next if /^content-length$/i =~ key
name = "HTTP_" + key
name.gsub!(/-/o, "_")
name.upcase!
meta[name] = val
}
meta
end
private
# :stopdoc:
MAX_URI_LENGTH = 2083 # :nodoc:
# same as Mongrel, Thin and Puma
MAX_HEADER_LENGTH = (112 * 1024) # :nodoc:
def read_request_line(socket)
@request_line = read_line(socket, MAX_URI_LENGTH) if socket
raise HTTPStatus::EOFError unless @request_line
@request_bytes = @request_line.bytesize
if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
raise HTTPStatus::RequestURITooLarge
end
@request_time = Time.now
if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
@request_method = $1
@unparsed_uri = $2
@http_version = HTTPVersion.new($3 ? $3 : "0.9")
else
rl = @request_line.sub(/\x0d?\x0a\z/o, '')
raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'."
end
end
def read_header(socket)
if socket
while line = read_line(socket)
break if /\A(#{CRLF}|#{LF})\z/om =~ line
if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH
raise HTTPStatus::RequestEntityTooLarge, 'headers too large'
end
@raw_header << line
end
end
@header = HTTPUtils::parse_header(@raw_header.join)
end
def parse_uri(str, scheme="http")
if @config[:Escape8bitURI]
str = HTTPUtils::escape8bit(str)
end
str.sub!(%r{\A/+}o, '/')
uri = URI::parse(str)
return uri if uri.absolute?
if @forwarded_host
host, port = @forwarded_host, @forwarded_port
elsif self["host"]
host, port = parse_host_request_line(self["host"])
elsif @addr.size > 0
host, port = @addr[2], @addr[1]
else
host, port = @config[:ServerName], @config[:Port]
end
uri.scheme = @forwarded_proto || scheme
uri.host = host
uri.port = port ? port.to_i : nil
return URI::parse(uri.to_s)
end
def parse_host_request_line(host)
pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/no
host.scan(pattern)[0]
end
def read_body(socket, block)
return unless socket
if tc = self['transfer-encoding']
case tc
when /\Achunked\z/io then read_chunked(socket, block)
else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
end
elsif self['content-length'] || @remaining_size
@remaining_size ||= self['content-length'].to_i
while @remaining_size > 0
sz = [@buffer_size, @remaining_size].min
break unless buf = read_data(socket, sz)
@remaining_size -= buf.bytesize
block.call(buf)
end
if @remaining_size > 0 && @socket.eof?
raise HTTPStatus::BadRequest, "invalid body size."
end
elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
raise HTTPStatus::LengthRequired
end
return @body
end
def read_chunk_size(socket)
line = read_line(socket)
if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
chunk_size = $1.hex
chunk_ext = $2
[ chunk_size, chunk_ext ]
else
raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
end
end
def read_chunked(socket, block)
chunk_size, = read_chunk_size(socket)
while chunk_size > 0
begin
sz = [ chunk_size, @buffer_size ].min
data = read_data(socket, sz) # read chunk-data
if data.nil? || data.bytesize != sz
raise HTTPStatus::BadRequest, "bad chunk data size."
end
block.call(data)
end while (chunk_size -= sz) > 0
read_line(socket) # skip CRLF
chunk_size, = read_chunk_size(socket)
end
read_header(socket) # trailer + CRLF
@header.delete("transfer-encoding")
@remaining_size = 0
end
def _read_data(io, method, *arg)
begin
WEBrick::Utils.timeout(@config[:RequestTimeout]){
return io.__send__(method, *arg)
}
rescue Errno::ECONNRESET
return nil
rescue Timeout::Error
raise HTTPStatus::RequestTimeout
end
end
def read_line(io, size=4096)
_read_data(io, :gets, LF, size)
end
def read_data(io, size)
_read_data(io, :read, size)
end
def parse_query()
begin
if @request_method == "GET" || @request_method == "HEAD"
@query = HTTPUtils::parse_query(@query_string)
elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
@query = HTTPUtils::parse_query(body)
elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
boundary = HTTPUtils::dequote($1)
@query = HTTPUtils::parse_form_data(body, boundary)
else
@query = Hash.new
end
rescue => ex
raise HTTPStatus::BadRequest, ex.message
end
end
PrivateNetworkRegexp = /
^unknown$|
^((::ffff:)?127.0.0.1|::1)$|
^(::ffff:)?(10|172\.(1[6-9]|2[0-9]|3[01])|192\.168)\.
/ixo
# It's said that all X-Forwarded-* headers will contain more than one
# (comma-separated) value if the original request already contained one of
# these headers. Since we could use these values as Host header, we choose
# the initial(first) value. (apr_table_mergen() adds new value after the
# existing value with ", " prefix)
def setup_forwarded_info
if @forwarded_server = self["x-forwarded-server"]
@forwarded_server = @forwarded_server.split(",", 2).first
end
if @forwarded_proto = self["x-forwarded-proto"]
@forwarded_proto = @forwarded_proto.split(",", 2).first
end
if host_port = self["x-forwarded-host"]
host_port = host_port.split(",", 2).first
if host_port =~ /\A(\[[0-9a-fA-F:]+\])(?::(\d+))?\z/
@forwarded_host = $1
tmp = $2
else
@forwarded_host, tmp = host_port.split(":", 2)
end
@forwarded_port = (tmp || (@forwarded_proto == "https" ? 443 : 80)).to_i
end
if addrs = self["x-forwarded-for"]
addrs = addrs.split(",").collect(&:strip)
addrs.reject!{|ip| PrivateNetworkRegexp =~ ip }
@forwarded_for = addrs.first
end
end
# :startdoc:
end
end

View File

@ -1,588 +0,0 @@
# frozen_string_literal: true
#
# httpresponse.rb -- HTTPResponse Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
require 'time'
require 'uri'
require_relative 'httpversion'
require_relative 'htmlutils'
require_relative 'httputils'
require_relative 'httpstatus'
module WEBrick
##
# An HTTP response. This is filled in by the service or do_* methods of a
# WEBrick HTTP Servlet.
class HTTPResponse
class InvalidHeader < StandardError
end
##
# HTTP Response version
attr_reader :http_version
##
# Response status code (200)
attr_reader :status
##
# Response header
attr_reader :header
##
# Response cookies
attr_reader :cookies
##
# Response reason phrase ("OK")
attr_accessor :reason_phrase
##
# Body may be:
# * a String;
# * an IO-like object that responds to +#read+ and +#readpartial+;
# * a Proc-like object that responds to +#call+.
#
# In the latter case, either #chunked= should be set to +true+,
# or <code>header['content-length']</code> explicitly provided.
# Example:
#
# server.mount_proc '/' do |req, res|
# res.chunked = true
# # or
# # res.header['content-length'] = 10
# res.body = proc { |out| out.write(Time.now.to_s) }
# end
attr_accessor :body
##
# Request method for this response
attr_accessor :request_method
##
# Request URI for this response
attr_accessor :request_uri
##
# Request HTTP version for this response
attr_accessor :request_http_version
##
# Filename of the static file in this response. Only used by the
# FileHandler servlet.
attr_accessor :filename
##
# Is this a keep-alive response?
attr_accessor :keep_alive
##
# Configuration for this response
attr_reader :config
##
# Bytes sent in this response
attr_reader :sent_size
##
# Set the response body proc as an streaming/upgrade response.
attr_accessor :upgrade
##
# Creates a new HTTP response object. WEBrick::Config::HTTP is the
# default configuration.
def initialize(config)
@config = config
@buffer_size = config[:OutputBufferSize]
@logger = config[:Logger]
@header = Hash.new
@status = HTTPStatus::RC_OK
@reason_phrase = nil
@http_version = HTTPVersion::convert(@config[:HTTPVersion])
@body = +""
@keep_alive = true
@cookies = []
@request_method = nil
@request_uri = nil
@request_http_version = @http_version # temporary
@chunked = false
@filename = nil
@sent_size = 0
@bodytempfile = nil
end
##
# The response's HTTP status line
def status_line
"HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF
end
##
# Sets the response's status to the +status+ code
def status=(status)
@status = status
@reason_phrase = HTTPStatus::reason_phrase(status)
end
##
# Retrieves the response header +field+
def [](field)
@header[field.downcase]
end
##
# Sets the response header +field+ to +value+
def []=(field, value)
@chunked = value.to_s.downcase == 'chunked' if field.downcase == 'transfer-encoding'
@header[field.downcase] = value.to_s
end
##
# The content-length header
def content_length
if len = self['content-length']
return Integer(len)
end
end
##
# Sets the content-length header to +len+
def content_length=(len)
self['content-length'] = len.to_s
end
##
# The content-type header
def content_type
self['content-type']
end
##
# Sets the content-type header to +type+
def content_type=(type)
self['content-type'] = type
end
##
# Iterates over each header in the response
def each
@header.each{|field, value| yield(field, value) }
end
##
# Will this response body be returned using chunked transfer-encoding?
def chunked?
@chunked
end
##
# Enables chunked transfer encoding.
def chunked=(val)
@chunked = val ? true : false
end
##
# Will this response's connection be kept alive?
def keep_alive?
@keep_alive
end
##
# Sets the response to be a streaming/upgrade response.
# This will disable keep-alive and chunked transfer encoding.
def upgrade!(protocol)
@upgrade = protocol
@keep_alive = false
@chunked = false
end
##
# Sends the response on +socket+
def send_response(socket) # :nodoc:
begin
setup_header()
send_header(socket)
send_body(socket)
rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
@logger.debug(ex)
@keep_alive = false
rescue Exception => ex
@logger.error(ex)
@keep_alive = false
end
end
##
# Sets up the headers for sending
def setup_header() # :nodoc:
@reason_phrase ||= HTTPStatus::reason_phrase(@status)
@header['server'] ||= @config[:ServerSoftware]
@header['date'] ||= Time.now.httpdate
if @upgrade
@header['connection'] = 'upgrade'
@header['upgrade'] = @upgrade
@keep_alive = false
return
end
# HTTP/0.9 features
if @request_http_version < "1.0"
@http_version = HTTPVersion.new("0.9")
@keep_alive = false
end
# HTTP/1.0 features
if @request_http_version < "1.1"
if chunked?
@chunked = false
ver = @request_http_version.to_s
msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
@logger.warn(msg)
end
end
# Determine the message length (RFC2616 -- 4.4 Message Length)
if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
@header.delete('content-length')
@body = ""
elsif chunked?
@header["transfer-encoding"] = "chunked"
@header.delete('content-length')
elsif %r{^multipart/byteranges} =~ @header['content-type']
@header.delete('content-length')
elsif @header['content-length'].nil?
if @body.respond_to?(:bytesize)
@header['content-length'] = @body.bytesize.to_s
else
@header['connection'] = 'close'
end
end
# Keep-Alive connection.
if @header['connection'] == "close"
@keep_alive = false
elsif keep_alive?
if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status)
@header['connection'] = "Keep-Alive"
else
msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true"
@logger.warn(msg)
@header['connection'] = "close"
@keep_alive = false
end
else
@header['connection'] = "close"
end
# Location is a single absoluteURI.
if location = @header['location']
if @request_uri
@header['location'] = @request_uri.merge(location).to_s
end
end
end
def make_body_tempfile # :nodoc:
return if @bodytempfile
bodytempfile = Tempfile.create("webrick")
if @body.nil?
# nothing
elsif @body.respond_to? :readpartial
IO.copy_stream(@body, bodytempfile)
@body.close
elsif @body.respond_to? :call
@body.call(bodytempfile)
else
bodytempfile.write @body
end
bodytempfile.rewind
@body = @bodytempfile = bodytempfile
@header['content-length'] = bodytempfile.stat.size.to_s
end
def remove_body_tempfile # :nodoc:
if @bodytempfile
@bodytempfile.close
File.unlink @bodytempfile.path
@bodytempfile = nil
end
end
##
# Sends the headers on +socket+
def send_header(socket) # :nodoc:
if @http_version.major > 0
data = status_line().dup
@header.each{|key, value|
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
data << "#{tmp}: #{check_header(value)}" << CRLF
}
@cookies.each{|cookie|
data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
}
data << CRLF
socket.write(data)
end
rescue InvalidHeader => e
@header.clear
@cookies.clear
set_error e
retry
end
##
# Sends the body on +socket+
def send_body(socket) # :nodoc:
if @body.respond_to? :readpartial then
send_body_io(socket)
elsif @body.respond_to?(:call) then
send_body_proc(socket)
else
send_body_string(socket)
end
end
##
# Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
#
# Example:
#
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
def set_redirect(status, url)
url = URI(url).to_s
@body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
@header['location'] = url
raise status
end
##
# Creates an error page for exception +ex+ with an optional +backtrace+
def set_error(ex, backtrace=false)
case ex
when HTTPStatus::Status
@keep_alive = false if HTTPStatus::error?(ex.code)
self.status = ex.code
else
@keep_alive = false
self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
end
@header['content-type'] = "text/html; charset=ISO-8859-1"
if respond_to?(:create_error_page)
create_error_page()
return
end
if @request_uri
host, port = @request_uri.host, @request_uri.port
else
host, port = @config[:ServerName], @config[:Port]
end
error_body(backtrace, ex, host, port)
end
private
def check_header(header_value)
header_value = header_value.to_s
if /[\r\n]/ =~ header_value
raise InvalidHeader
else
header_value
end
end
# :stopdoc:
def error_body(backtrace, ex, host, port)
@body = +""
@body << <<-_end_of_html_
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<HTML>
<HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
<BODY>
<H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
#{HTMLUtils::escape(ex.message)}
<HR>
_end_of_html_
if backtrace && $DEBUG
@body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
@body << "#{HTMLUtils::escape(ex.message)}"
@body << "<PRE>"
ex.backtrace.each{|line| @body << "\t#{line}\n"}
@body << "</PRE><HR>"
end
@body << <<-_end_of_html_
<ADDRESS>
#{HTMLUtils::escape(@config[:ServerSoftware])} at
#{host}:#{port}
</ADDRESS>
</BODY>
</HTML>
_end_of_html_
end
def send_body_io(socket)
begin
if @request_method == "HEAD"
# do nothing
elsif chunked?
buf = +''
begin
@body.readpartial(@buffer_size, buf)
size = buf.bytesize
data = +"#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
socket.write(data)
data.clear
@sent_size += size
rescue EOFError
break
end while true
buf.clear
socket.write("0#{CRLF}#{CRLF}")
else
if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
offset = $1.to_i
size = $2.to_i - offset + 1
else
offset = nil
size = @header['content-length']
size = size.to_i if size
end
begin
@sent_size = IO.copy_stream(@body, socket, size, offset)
rescue NotImplementedError
@body.seek(offset, IO::SEEK_SET)
@sent_size = IO.copy_stream(@body, socket, size)
end
end
ensure
@body.close
end
remove_body_tempfile
end
def send_body_string(socket)
if @request_method == "HEAD"
# do nothing
elsif chunked?
body ? @body.bytesize : 0
while buf = @body[@sent_size, @buffer_size]
break if buf.empty?
size = buf.bytesize
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
buf.clear
socket.write(data)
@sent_size += size
end
socket.write("0#{CRLF}#{CRLF}")
else
if @body && @body.bytesize > 0
socket.write(@body)
@sent_size = @body.bytesize
end
end
end
def send_body_proc(socket)
if @request_method == "HEAD"
# do nothing
elsif chunked?
@body.call(ChunkedWrapper.new(socket, self))
socket.write("0#{CRLF}#{CRLF}")
else
if @bodytempfile
@bodytempfile.rewind
IO.copy_stream(@bodytempfile, socket)
else
@body.call(socket)
end
if content_length = @header['content-length']
@sent_size = content_length.to_i
end
end
end
class ChunkedWrapper
def initialize(socket, resp)
@socket = socket
@resp = resp
end
def write(buf)
return 0 if buf.empty?
socket = @socket
@resp.instance_eval {
size = buf.bytesize
data = +"#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
socket.write(data)
data.clear
@sent_size += size
size
}
end
def <<(*buf)
write(buf)
self
end
end
# preserved for compatibility with some 3rd-party handlers
def _write_data(socket, data)
socket << data
end
# :startdoc:
end
end

View File

@ -1,152 +0,0 @@
# frozen_string_literal: true
#
# https.rb -- SSL/TLS enhancement for HTTPServer
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
require_relative 'ssl'
require_relative 'httpserver'
module WEBrick
module Config
HTTP.update(SSL)
end
##
#--
# Adds SSL functionality to WEBrick::HTTPRequest
class HTTPRequest
##
# HTTP request SSL cipher
attr_reader :cipher
##
# HTTP request server certificate
attr_reader :server_cert
##
# HTTP request client certificate
attr_reader :client_cert
# :stopdoc:
alias orig_parse parse
def parse(socket=nil)
if socket.respond_to?(:cert)
@server_cert = socket.cert || @config[:SSLCertificate]
@client_cert = socket.peer_cert
@client_cert_chain = socket.peer_cert_chain
@cipher = socket.cipher
end
orig_parse(socket)
end
alias orig_parse_uri parse_uri
def parse_uri(str, scheme="https")
if server_cert
return orig_parse_uri(str, scheme)
end
return orig_parse_uri(str)
end
private :parse_uri
alias orig_meta_vars meta_vars
def meta_vars
meta = orig_meta_vars
if server_cert
meta["HTTPS"] = "on"
meta["SSL_SERVER_CERT"] = @server_cert.to_pem
meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
if @client_cert_chain
@client_cert_chain.each_with_index{|cert, i|
meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem
}
end
meta["SSL_CIPHER"] = @cipher[0]
meta["SSL_PROTOCOL"] = @cipher[1]
meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s
meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s
end
meta
end
# :startdoc:
end
##
#--
# Fake WEBrick::HTTPRequest for lookup_server
class SNIRequest
##
# The SNI hostname
attr_reader :host
##
# The socket address of the server
attr_reader :addr
##
# The port this request is for
attr_reader :port
##
# Creates a new SNIRequest.
def initialize(sslsocket, hostname)
@host = hostname
@addr = sslsocket.addr
@port = @addr[1]
end
end
##
#--
# Adds SSL functionality to WEBrick::HTTPServer
class HTTPServer < ::WEBrick::GenericServer
##
# ServerNameIndication callback
def ssl_servername_callback(sslsocket, hostname = nil)
req = SNIRequest.new(sslsocket, hostname)
server = lookup_server(req)
server ? server.ssl_context : nil
end
# :stopdoc:
##
# Check whether +server+ is also SSL server.
# Also +server+'s SSL context will be created.
alias orig_virtual_host virtual_host
def virtual_host(server)
if @config[:SSLEnable] && !server.ssl_context
raise ArgumentError, "virtual host must set SSLEnable to true"
end
orig_virtual_host(server)
end
# :startdoc:
end
end

View File

@ -1,294 +0,0 @@
# frozen_string_literal: true
#
# httpserver.rb -- HTTPServer Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
require 'io/wait'
require_relative 'server'
require_relative 'httputils'
require_relative 'httpstatus'
require_relative 'httprequest'
require_relative 'httpresponse'
require_relative 'httpservlet'
require_relative 'accesslog'
module WEBrick
class HTTPServerError < ServerError; end
##
# An HTTP Server
class HTTPServer < ::WEBrick::GenericServer
##
# Creates a new HTTP server according to +config+
#
# An HTTP server uses the following attributes:
#
# :AccessLog:: An array of access logs. See WEBrick::AccessLog
# :BindAddress:: Local address for the server to bind to
# :DocumentRoot:: Root path to serve files from
# :DocumentRootOptions:: Options for the default HTTPServlet::FileHandler
# :HTTPVersion:: The HTTP version of this server
# :Port:: Port to listen on
# :RequestCallback:: Called with a request and response before each
# request is serviced.
# :RequestTimeout:: Maximum time to wait between requests
# :ServerAlias:: Array of alternate names for this server for virtual
# hosting
# :ServerName:: Name for this server for virtual hosting
def initialize(config={}, default=Config::HTTP)
super(config, default)
@http_version = HTTPVersion::convert(@config[:HTTPVersion])
@mount_tab = MountTable.new
if @config[:DocumentRoot]
mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot],
@config[:DocumentRootOptions])
end
unless @config[:AccessLog]
@config[:AccessLog] = [
[ $stderr, AccessLog::COMMON_LOG_FORMAT ],
[ $stderr, AccessLog::REFERER_LOG_FORMAT ]
]
end
@virtual_hosts = Array.new
end
##
# Processes requests on +sock+
def run(sock)
while true
req = create_request(@config)
res = create_response(@config)
server = self
begin
timeout = @config[:RequestTimeout]
while timeout > 0
break if sock.to_io.wait_readable(0.5)
break if @status != :Running
timeout -= 0.5
end
raise HTTPStatus::EOFError if timeout <= 0 || @status != :Running
raise HTTPStatus::EOFError if sock.eof?
req.parse(sock)
res.request_method = req.request_method
res.request_uri = req.request_uri
res.request_http_version = req.http_version
res.keep_alive = req.keep_alive?
server = lookup_server(req) || self
if callback = server[:RequestCallback]
callback.call(req, res)
elsif callback = server[:RequestHandler]
msg = ":RequestHandler is deprecated, please use :RequestCallback"
@logger.warn(msg)
callback.call(req, res)
end
server.service(req, res)
rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
res.set_error(ex)
rescue HTTPStatus::Error => ex
@logger.error(ex.message)
res.set_error(ex)
rescue HTTPStatus::Status => ex
res.status = ex.code
rescue StandardError => ex
@logger.error(ex)
res.set_error(ex, true)
ensure
if req.request_line
if req.keep_alive? && res.keep_alive?
req.fixup()
end
res.send_response(sock)
server.access_log(@config, req, res)
end
end
break if @http_version < "1.1"
break unless req.keep_alive?
break unless res.keep_alive?
end
end
##
# Services +req+ and fills in +res+
def service(req, res)
if req.unparsed_uri == "*"
if req.request_method == "OPTIONS"
do_OPTIONS(req, res)
raise HTTPStatus::OK
end
raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
end
servlet, options, script_name, path_info = search_servlet(req.path)
raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
req.script_name = script_name
req.path_info = path_info
si = servlet.get_instance(self, *options)
@logger.debug(format("%s is invoked.", si.class.name))
si.service(req, res)
end
##
# The default OPTIONS request handler says GET, HEAD, POST and OPTIONS
# requests are allowed.
def do_OPTIONS(req, res)
res["allow"] = "GET,HEAD,POST,OPTIONS"
end
##
# Mounts +servlet+ on +dir+ passing +options+ to the servlet at creation
# time
def mount(dir, servlet, *options)
@logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
@mount_tab[dir] = [ servlet, options ]
end
##
# Mounts +proc+ or +block+ on +dir+ and calls it with a
# WEBrick::HTTPRequest and WEBrick::HTTPResponse
def mount_proc(dir, proc=nil, &block)
proc ||= block
raise HTTPServerError, "must pass a proc or block" unless proc
mount(dir, HTTPServlet::ProcHandler.new(proc))
end
##
# Unmounts +dir+
def unmount(dir)
@logger.debug(sprintf("unmount %s.", dir))
@mount_tab.delete(dir)
end
alias umount unmount
##
# Finds a servlet for +path+
def search_servlet(path)
script_name, path_info = @mount_tab.scan(path)
servlet, options = @mount_tab[script_name]
if servlet
[ servlet, options, script_name, path_info ]
end
end
##
# Adds +server+ as a virtual host.
def virtual_host(server)
@virtual_hosts << server
@virtual_hosts = @virtual_hosts.sort_by{|s|
num = 0
num -= 4 if s[:BindAddress]
num -= 2 if s[:Port]
num -= 1 if s[:ServerName]
num
}
end
##
# Finds the appropriate virtual host to handle +req+
def lookup_server(req)
@virtual_hosts.find{|s|
(s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
(s[:Port].nil? || req.port == s[:Port]) &&
((s[:ServerName].nil? || req.host == s[:ServerName]) ||
(!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
}
end
##
# Logs +req+ and +res+ in the access logs. +config+ is used for the
# server name.
def access_log(config, req, res)
param = AccessLog::setup_params(config, req, res)
@config[:AccessLog].each{|logger, fmt|
logger << AccessLog::format(fmt+"\n", param)
}
end
##
# Creates the HTTPRequest used when handling the HTTP
# request. Can be overridden by subclasses.
def create_request(with_webrick_config)
HTTPRequest.new(with_webrick_config)
end
##
# Creates the HTTPResponse used when handling the HTTP
# request. Can be overridden by subclasses.
def create_response(with_webrick_config)
HTTPResponse.new(with_webrick_config)
end
##
# Mount table for the path a servlet is mounted on in the directory space
# of the server. Users of WEBrick can only access this indirectly via
# WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and
# WEBrick::HTTPServer#search_servlet
class MountTable # :nodoc:
def initialize
@tab = Hash.new
compile
end
def [](dir)
dir = normalize(dir)
@tab[dir]
end
def []=(dir, val)
dir = normalize(dir)
@tab[dir] = val
compile
val
end
def delete(dir)
dir = normalize(dir)
res = @tab.delete(dir)
compile
res
end
def scan(path)
@scanner =~ path
[ $&, $' ]
end
private
def compile
k = @tab.keys
k.sort!
k.reverse!
k.collect!{|path| Regexp.escape(path) }
@scanner = Regexp.new("\\A(" + k.join("|") +")(?=/|\\z)")
end
def normalize(dir)
ret = dir ? dir.dup : +""
ret.sub!(%r|/+\z|, "")
ret
end
end
end
end

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
#
# httpservlet.rb -- HTTPServlet Utility File
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $
require_relative 'httpservlet/abstract'
require_relative 'httpservlet/filehandler'
require_relative 'httpservlet/cgihandler'
require_relative 'httpservlet/erbhandler'
require_relative 'httpservlet/prochandler'
module WEBrick
module HTTPServlet
FileHandler.add_handler("cgi", CGIHandler)
FileHandler.add_handler("rhtml", ERBHandler)
end
end

View File

@ -1,152 +0,0 @@
# frozen_string_literal: true
#
# httpservlet.rb -- HTTPServlet Module
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $
require_relative '../htmlutils'
require_relative '../httputils'
require_relative '../httpstatus'
module WEBrick
module HTTPServlet
class HTTPServletError < StandardError; end
##
# AbstractServlet allows HTTP server modules to be reused across multiple
# servers and allows encapsulation of functionality.
#
# By default a servlet will respond to GET, HEAD (through an alias to GET)
# and OPTIONS requests.
#
# By default a new servlet is initialized for every request. A servlet
# instance can be reused by overriding ::get_instance in the
# AbstractServlet subclass.
#
# == A Simple Servlet
#
# class Simple < WEBrick::HTTPServlet::AbstractServlet
# def do_GET request, response
# status, content_type, body = do_stuff_with request
#
# response.status = status
# response['Content-Type'] = content_type
# response.body = body
# end
#
# def do_stuff_with request
# return 200, 'text/plain', 'you got a page'
# end
# end
#
# This servlet can be mounted on a server at a given path:
#
# server.mount '/simple', Simple
#
# == Servlet Configuration
#
# Servlets can be configured via initialize. The first argument is the
# HTTP server the servlet is being initialized for.
#
# class Configurable < Simple
# def initialize server, color, size
# super server
# @color = color
# @size = size
# end
#
# def do_stuff_with request
# content = "<p " \
# %q{style="color: #{@color}; font-size: #{@size}"} \
# ">Hello, World!"
#
# return 200, "text/html", content
# end
# end
#
# This servlet must be provided two arguments at mount time:
#
# server.mount '/configurable', Configurable, 'red', '2em'
class AbstractServlet
##
# Factory for servlet instances that will handle a request from +server+
# using +options+ from the mount point. By default a new servlet
# instance is created for every call.
def self.get_instance(server, *options)
self.new(server, *options)
end
##
# Initializes a new servlet for +server+ using +options+ which are
# stored as-is in +@options+. +@logger+ is also provided.
def initialize(server, *options)
@server = @config = server
@logger = @server[:Logger]
@options = options
end
##
# Dispatches to a +do_+ method based on +req+ if such a method is
# available. (+do_GET+ for a GET request). Raises a MethodNotAllowed
# exception if the method is not implemented.
def service(req, res)
method_name = "do_" + req.request_method.gsub(/-/, "_")
if respond_to?(method_name)
__send__(method_name, req, res)
else
raise HTTPStatus::MethodNotAllowed,
"unsupported method `#{req.request_method}'."
end
end
##
# Raises a NotFound exception
def do_GET(req, res)
raise HTTPStatus::NotFound, "not found."
end
##
# Dispatches to do_GET
def do_HEAD(req, res)
do_GET(req, res)
end
##
# Returns the allowed HTTP request methods
def do_OPTIONS(req, res)
m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1}
m.sort!
res["allow"] = m.join(",")
end
private
##
# Redirects to a path ending in /
def redirect_to_directory_uri(req, res)
if req.path[-1] != ?/
location = WEBrick::HTTPUtils.escape_path(req.path + "/")
if req.query_string && req.query_string.bytesize > 0
location << "?" << req.query_string
end
res.set_redirect(HTTPStatus::MovedPermanently, location)
end
end
end
end
end

View File

@ -1,47 +0,0 @@
# frozen_string_literal: true
#
# cgi_runner.rb -- CGI launcher.
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $
def sysread(io, size)
buf = +""
while size > 0
tmp = io.sysread(size)
buf << tmp
size -= tmp.bytesize
end
return buf
end
STDIN.binmode
len = sysread(STDIN, 8).to_i
out = sysread(STDIN, len)
STDOUT.reopen(File.open(out, "w"))
len = sysread(STDIN, 8).to_i
err = sysread(STDIN, len)
STDERR.reopen(File.open(err, "w"))
len = sysread(STDIN, 8).to_i
dump = sysread(STDIN, len)
hash = Marshal.restore(dump)
ENV.keys.each{|name| ENV.delete(name) }
hash.each{|k, v| ENV[k] = v if v }
dir = File::dirname(ENV["SCRIPT_FILENAME"])
Dir::chdir dir
if ARGV[0]
argv = ARGV.dup
argv << ENV["SCRIPT_FILENAME"]
exec(*argv)
# NOTREACHED
end
exec ENV["SCRIPT_FILENAME"]

View File

@ -1,126 +0,0 @@
# frozen_string_literal: true
#
# cgihandler.rb -- CGIHandler Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
require 'rbconfig'
require 'tempfile'
require_relative '../config'
require_relative 'abstract'
module WEBrick
module HTTPServlet
##
# Servlet for handling CGI scripts
#
# Example:
#
# server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler,
# '/path/to/my_script')
class CGIHandler < AbstractServlet
Ruby = RbConfig.ruby # :nodoc:
CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
CGIRunnerArray = [Ruby, "#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb".freeze].freeze # :nodoc:
##
# Creates a new CGI script servlet for the script at +name+
def initialize(server, name)
super(server, name)
@script_filename = name
@tempdir = server[:TempDir]
interpreter = server[:CGIInterpreter]
if interpreter.is_a?(Array)
@cgicmd = CGIRunnerArray + interpreter
else
@cgicmd = "#{CGIRunner} #{interpreter}"
end
end
# :stopdoc:
def do_GET(req, res)
cgi_in = IO::popen(@cgicmd, "wb")
cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
cgi_out.set_encoding("ASCII-8BIT")
cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY)
cgi_err.set_encoding("ASCII-8BIT")
begin
cgi_in.sync = true
meta = req.meta_vars
meta["SCRIPT_FILENAME"] = @script_filename
meta["PATH"] = @config[:CGIPathEnv]
meta.delete("HTTP_PROXY")
if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
meta["SystemRoot"] = ENV["SystemRoot"]
end
dump = Marshal.dump(meta)
cgi_in.write("%8d" % cgi_out.path.bytesize)
cgi_in.write(cgi_out.path)
cgi_in.write("%8d" % cgi_err.path.bytesize)
cgi_in.write(cgi_err.path)
cgi_in.write("%8d" % dump.bytesize)
cgi_in.write(dump)
req.body { |chunk| cgi_in.write(chunk) }
ensure
cgi_in.close
status = $?.exitstatus
sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
data = cgi_out.read
cgi_out.close(true)
if errmsg = cgi_err.read
if errmsg.bytesize > 0
@logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
end
end
cgi_err.close(true)
end
if status != 0
@logger.error("CGIHandler: #{@script_filename} exit with #{status}")
end
data = "" unless data
raw_header, body = data.split(/^[\xd\xa]+/, 2)
raise HTTPStatus::InternalServerError,
"Premature end of script headers: #{@script_filename}" if body.nil?
begin
header = HTTPUtils::parse_header(raw_header)
if /^(\d+)/ =~ header['status'][0]
res.status = $1.to_i
header.delete('status')
end
if header.has_key?('location')
# RFC 3875 6.2.3, 6.2.4
res.status = 302 unless (300...400) === res.status
end
if header.has_key?('set-cookie')
header['set-cookie'].each{|k|
res.cookies << Cookie.parse_set_cookie(k)
}
header.delete('set-cookie')
end
header.each{|key, val| res[key] = val.join(", ") }
rescue => ex
raise HTTPStatus::InternalServerError, ex.message
end
res.body = body
end
alias do_POST do_GET
# :startdoc:
end
end
end

View File

@ -1,88 +0,0 @@
# frozen_string_literal: true
#
# erbhandler.rb -- ERBHandler Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
require_relative 'abstract'
require 'erb'
module WEBrick
module HTTPServlet
##
# ERBHandler evaluates an ERB file and returns the result. This handler
# is automatically used if there are .rhtml files in a directory served by
# the FileHandler.
#
# ERBHandler supports GET and POST methods.
#
# The ERB file is evaluated with the local variables +servlet_request+ and
# +servlet_response+ which are a WEBrick::HTTPRequest and
# WEBrick::HTTPResponse respectively.
#
# Example .rhtml file:
#
# Request to <%= servlet_request.request_uri %>
#
# Query params <%= servlet_request.query.inspect %>
class ERBHandler < AbstractServlet
##
# Creates a new ERBHandler on +server+ that will evaluate and serve the
# ERB file +name+
def initialize(server, name)
super(server, name)
@script_filename = name
end
##
# Handles GET requests
def do_GET(req, res)
unless defined?(ERB)
@logger.warn "#{self.class}: ERB not defined."
raise HTTPStatus::Forbidden, "ERBHandler cannot work."
end
begin
data = File.open(@script_filename, &:read)
res.body = evaluate(ERB.new(data), req, res)
res['content-type'] ||=
HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
rescue StandardError
raise
rescue Exception => ex
@logger.error(ex)
raise HTTPStatus::InternalServerError, ex.message
end
end
##
# Handles POST requests
alias do_POST do_GET
private
##
# Evaluates +erb+ providing +servlet_request+ and +servlet_response+ as
# local variables.
def evaluate(erb, servlet_request, servlet_response)
Module.new.module_eval{
servlet_request.meta_vars
servlet_request.query
erb.result(binding)
}
end
end
end
end

View File

@ -1,552 +0,0 @@
# frozen_string_literal: true
#
# filehandler.rb -- FileHandler Module
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $
require 'time'
require_relative '../htmlutils'
require_relative '../httputils'
require_relative '../httpstatus'
module WEBrick
module HTTPServlet
##
# Servlet for serving a single file. You probably want to use the
# FileHandler servlet instead as it handles directories and fancy indexes.
#
# Example:
#
# server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
# '/path/to/my_page.txt')
#
# This servlet handles If-Modified-Since and Range requests.
class DefaultFileHandler < AbstractServlet
##
# Creates a DefaultFileHandler instance for the file at +local_path+.
def initialize(server, local_path)
super(server, local_path)
@local_path = local_path
end
# :stopdoc:
def do_GET(req, res)
st = File::stat(@local_path)
mtime = st.mtime
res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i)
if not_modified?(req, res, mtime, res['etag'])
res.body = ''
raise HTTPStatus::NotModified
elsif req['range']
make_partial_content(req, res, @local_path, st.size)
raise HTTPStatus::PartialContent
else
mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
res['content-type'] = mtype
res['content-length'] = st.size.to_s
res['last-modified'] = mtime.httpdate
res.body = File.open(@local_path, "rb")
end
end
def not_modified?(req, res, mtime, etag)
if ir = req['if-range']
begin
if Time.httpdate(ir) >= mtime
return true
end
rescue
if HTTPUtils::split_header_value(ir).member?(res['etag'])
return true
end
end
end
if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime
return true
end
if (inm = req['if-none-match']) &&
HTTPUtils::split_header_value(inm).member?(res['etag'])
return true
end
return false
end
# returns a lambda for webrick/httpresponse.rb send_body_proc
def multipart_body(body, parts, boundary, mtype, filesize)
lambda do |socket|
begin
begin
first = parts.shift
last = parts.shift
socket.write(
"--#{boundary}#{CRLF}" \
"Content-Type: #{mtype}#{CRLF}" \
"Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \
"#{CRLF}"
)
begin
IO.copy_stream(body, socket, last - first + 1, first)
rescue NotImplementedError
body.seek(first, IO::SEEK_SET)
IO.copy_stream(body, socket, last - first + 1)
end
socket.write(CRLF)
end while parts[0]
socket.write("--#{boundary}--#{CRLF}")
ensure
body.close
end
end
end
def make_partial_content(req, res, filename, filesize)
mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
unless ranges = HTTPUtils::parse_range_header(req['range'])
raise HTTPStatus::BadRequest,
"Unrecognized range-spec: \"#{req['range']}\""
end
File.open(filename, "rb"){|io|
if ranges.size > 1
time = Time.now
boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
parts = []
ranges.each {|range|
prange = prepare_range(range, filesize)
next if prange[0] < 0
parts.concat(prange)
}
raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty?
res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
if req.http_version < '1.1'
res['connection'] = 'close'
else
res.chunked = true
end
res.body = multipart_body(io.dup, parts, boundary, mtype, filesize)
elsif range = ranges[0]
first, last = prepare_range(range, filesize)
raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
res['content-type'] = mtype
res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
res['content-length'] = (last - first + 1).to_s
res.body = io.dup
else
raise HTTPStatus::BadRequest
end
}
end
def prepare_range(range, filesize)
first = range.first < 0 ? filesize + range.first : range.first
return -1, -1 if first < 0 || first >= filesize
last = range.last < 0 ? filesize + range.last : range.last
last = filesize - 1 if last >= filesize
return first, last
end
# :startdoc:
end
##
# Serves a directory including fancy indexing and a variety of other
# options.
#
# Example:
#
# server.mount('/assets', WEBrick::HTTPServlet::FileHandler,
# '/path/to/assets')
class FileHandler < AbstractServlet
HandlerTable = Hash.new # :nodoc:
##
# Allow custom handling of requests for files with +suffix+ by class
# +handler+
def self.add_handler(suffix, handler)
HandlerTable[suffix] = handler
end
##
# Remove custom handling of requests for files with +suffix+
def self.remove_handler(suffix)
HandlerTable.delete(suffix)
end
##
# Creates a FileHandler servlet on +server+ that serves files starting
# at directory +root+
#
# +options+ may be a Hash containing keys from
# WEBrick::Config::FileHandler or +true+ or +false+.
#
# If +options+ is true or false then +:FancyIndexing+ is enabled or
# disabled respectively.
def initialize(server, root, options={}, default=Config::FileHandler)
@config = server.config
@logger = @config[:Logger]
@root = File.expand_path(root)
if options == true || options == false
options = { :FancyIndexing => options }
end
@options = default.dup.update(options)
end
# :stopdoc:
def set_filesystem_encoding(str)
enc = Encoding.find('filesystem')
if enc == Encoding::US_ASCII
str.b
else
str.dup.force_encoding(enc)
end
end
def service(req, res)
# if this class is mounted on "/" and /~username is requested.
# we're going to override path information before invoking service.
if defined?(Etc) && @options[:UserDir] && req.script_name.empty?
if %r|^(/~([^/]+))| =~ req.path_info
script_name, user = $1, $2
path_info = $'
begin
passwd = Etc::getpwnam(user)
@root = File::join(passwd.dir, @options[:UserDir])
req.script_name = script_name
req.path_info = path_info
rescue
@logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed"
end
end
end
prevent_directory_traversal(req, res)
super(req, res)
end
def do_GET(req, res)
unless exec_handler(req, res)
set_dir_list(req, res)
end
end
def do_POST(req, res)
unless exec_handler(req, res)
raise HTTPStatus::NotFound, "`#{req.path}' not found."
end
end
def do_OPTIONS(req, res)
unless exec_handler(req, res)
super(req, res)
end
end
# ToDo
# RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV
#
# PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE
# LOCK UNLOCK
# RFC3253: Versioning Extensions to WebDAV
# (Web Distributed Authoring and Versioning)
#
# VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT
# MKWORKSPACE UPDATE LABEL MERGE ACTIVITY
private
def trailing_pathsep?(path)
# check for trailing path separator:
# File.dirname("/aaaa/bbbb/") #=> "/aaaa")
# File.dirname("/aaaa/bbbb/x") #=> "/aaaa/bbbb")
# File.dirname("/aaaa/bbbb") #=> "/aaaa")
# File.dirname("/aaaa/bbbbx") #=> "/aaaa")
return File.dirname(path) != File.dirname(path+"x")
end
def prevent_directory_traversal(req, res)
# Preventing directory traversal on Windows platforms;
# Backslashes (0x5c) in path_info are not interpreted as special
# character in URI notation. So the value of path_info should be
# normalize before accessing to the filesystem.
# dirty hack for filesystem encoding; in nature, File.expand_path
# should not be used for path normalization. [Bug #3345]
path = req.path_info.dup.force_encoding(Encoding.find("filesystem"))
if trailing_pathsep?(req.path_info)
# File.expand_path removes the trailing path separator.
# Adding a character is a workaround to save it.
# File.expand_path("/aaa/") #=> "/aaa"
# File.expand_path("/aaa/" + "x") #=> "/aaa/x"
expanded = File.expand_path(path + "x")
expanded.chop! # remove trailing "x"
else
expanded = File.expand_path(path)
end
expanded.force_encoding(req.path_info.encoding)
req.path_info = expanded
end
def exec_handler(req, res)
raise HTTPStatus::NotFound, "`#{req.path}' not found." unless @root
if set_filename(req, res)
handler = get_handler(req, res)
call_callback(:HandlerCallback, req, res)
h = handler.get_instance(@config, res.filename)
h.service(req, res)
return true
end
call_callback(:HandlerCallback, req, res)
return false
end
def get_handler(req, res)
suffix1 = (/\.(\w+)\z/ =~ res.filename) && $1.downcase
if /\.(\w+)\.([\w\-]+)\z/ =~ res.filename
if @options[:AcceptableLanguages].include?($2.downcase)
suffix2 = $1.downcase
end
end
handler_table = @options[:HandlerTable]
return handler_table[suffix1] || handler_table[suffix2] ||
HandlerTable[suffix1] || HandlerTable[suffix2] ||
DefaultFileHandler
end
def set_filename(req, res)
res.filename = @root
path_info = req.path_info.scan(%r|/[^/]*|)
path_info.unshift("") # dummy for checking @root dir
while base = path_info.first
base = set_filesystem_encoding(base)
break if base == "/"
break unless File.directory?(File.expand_path(res.filename + base))
shift_path_info(req, res, path_info)
call_callback(:DirectoryCallback, req, res)
end
if base = path_info.first
base = set_filesystem_encoding(base)
if base == "/"
if file = search_index_file(req, res)
shift_path_info(req, res, path_info, file)
call_callback(:FileCallback, req, res)
return true
end
shift_path_info(req, res, path_info)
elsif file = search_file(req, res, base)
shift_path_info(req, res, path_info, file)
call_callback(:FileCallback, req, res)
return true
else
raise HTTPStatus::NotFound, "`#{req.path}' not found."
end
end
return false
end
def check_filename(req, res, name)
if nondisclosure_name?(name) || windows_ambiguous_name?(name)
@logger.warn("the request refers nondisclosure name `#{name}'.")
raise HTTPStatus::NotFound, "`#{req.path}' not found."
end
end
def shift_path_info(req, res, path_info, base=nil)
tmp = path_info.shift
base = base || set_filesystem_encoding(tmp)
req.path_info = path_info.join
req.script_name << base
res.filename = File.expand_path(res.filename + base)
check_filename(req, res, File.basename(res.filename))
end
def search_index_file(req, res)
@config[:DirectoryIndex].each{|index|
if file = search_file(req, res, "/"+index)
return file
end
}
return nil
end
def search_file(req, res, basename)
langs = @options[:AcceptableLanguages]
path = res.filename + basename
if File.file?(path)
return basename
elsif langs.size > 0
req.accept_language.each{|lang|
path_with_lang = path + ".#{lang}"
if langs.member?(lang) && File.file?(path_with_lang)
return basename + ".#{lang}"
end
}
(langs - req.accept_language).each{|lang|
path_with_lang = path + ".#{lang}"
if File.file?(path_with_lang)
return basename + ".#{lang}"
end
}
end
return nil
end
def call_callback(callback_name, req, res)
if cb = @options[callback_name]
cb.call(req, res)
end
end
def windows_ambiguous_name?(name)
return true if /[. ]+\z/ =~ name
return true if /::\$DATA\z/ =~ name
return false
end
def nondisclosure_name?(name)
@options[:NondisclosureName].each{|pattern|
if File.fnmatch(pattern, name, File::FNM_CASEFOLD)
return true
end
}
return false
end
def set_dir_list(req, res)
redirect_to_directory_uri(req, res)
unless @options[:FancyIndexing]
raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'"
end
local_path = res.filename
list = Dir::entries(local_path).collect{|name|
next if name == "." || name == ".."
next if nondisclosure_name?(name)
next if windows_ambiguous_name?(name)
st = (File::stat(File.join(local_path, name)) rescue nil)
if st.nil?
[ name, nil, -1 ]
elsif st.directory?
[ name + "/", st.mtime, -1 ]
else
[ name, st.mtime, st.size ]
end
}
list.compact!
query = req.query
d0 = nil
idx = nil
%w[N M S].each_with_index do |q, i|
if d = query.delete(q)
idx ||= i
d0 ||= d
end
end
d0 ||= "A"
idx ||= 0
d1 = (d0 == "A") ? "D" : "A"
if d0 == "A"
list.sort!{|a,b| a[idx] <=> b[idx] }
else
list.sort!{|a,b| b[idx] <=> a[idx] }
end
namewidth = query["NameWidth"]
if namewidth == "*"
namewidth = nil
elsif !namewidth or (namewidth = namewidth.to_i) < 2
namewidth = 25
end
query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")}.dup
type = +"text/html"
case enc = Encoding.find('filesystem')
when Encoding::US_ASCII, Encoding::ASCII_8BIT
else
type << "; charset=\"#{enc.name}\""
end
res['content-type'] = type
title = "Index of #{HTMLUtils::escape(req.path)}"
res.body = +<<-_end_of_html_
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE>#{title}</TITLE>
<style type="text/css">
<!--
.name, .mtime { text-align: left; }
.size { text-align: right; }
td { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
table { border-collapse: collapse; }
tr th { border-bottom: 2px groove; }
//-->
</style>
</HEAD>
<BODY>
<H1>#{title}</H1>
_end_of_html_
res.body << "<TABLE width=\"100%\"><THEAD><TR>\n"
res.body << "<TH class=\"name\"><A HREF=\"?N=#{d1}#{query}\">Name</A></TH>"
res.body << "<TH class=\"mtime\"><A HREF=\"?M=#{d1}#{query}\">Last modified</A></TH>"
res.body << "<TH class=\"size\"><A HREF=\"?S=#{d1}#{query}\">Size</A></TH>\n"
res.body << "</TR></THEAD>\n"
res.body << "<TBODY>\n"
query.sub!(/\A&/, '?')
list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
list.each{ |name, time, size|
if name == ".."
dname = "Parent Directory"
elsif namewidth and name.size > namewidth
dname = name[0...(namewidth - 2)] << '..'
else
dname = name
end
s = +"<TR><TD class=\"name\"><A HREF=\"#{HTTPUtils::escape(name)}#{query if name.end_with?('/')}\">#{HTMLUtils::escape(dname)}</A></TD>"
s << "<TD class=\"mtime\">" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << "</TD>"
s << "<TD class=\"size\">" << (size >= 0 ? size.to_s : "-") << "</TD></TR>\n"
res.body << s
}
res.body << "</TBODY></TABLE>"
res.body << "<HR>"
res.body << <<-_end_of_html_
<ADDRESS>
#{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
at #{req.host}:#{req.port}
</ADDRESS>
</BODY>
</HTML>
_end_of_html_
end
# :startdoc:
end
end
end

View File

@ -1,48 +0,0 @@
# frozen_string_literal: true
#
# prochandler.rb -- ProcHandler Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
require_relative 'abstract'
module WEBrick
module HTTPServlet
##
# Mounts a proc at a path that accepts a request and response.
#
# Instead of mounting this servlet with WEBrick::HTTPServer#mount use
# WEBrick::HTTPServer#mount_proc:
#
# server.mount_proc '/' do |req, res|
# res.body = 'it worked!'
# res.status = 200
# end
class ProcHandler < AbstractServlet
# :stopdoc:
def get_instance(server, *options)
self
end
def initialize(proc)
@proc = proc
end
def do_GET(request, response)
@proc.call(request, response)
end
alias do_POST do_GET
alias do_PUT do_GET
# :startdoc:
end
end
end

View File

@ -1,194 +0,0 @@
# frozen_string_literal: true
#--
# httpstatus.rb -- HTTPStatus Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
require_relative 'accesslog'
module WEBrick
##
# This module is used to manager HTTP status codes.
#
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more
# information.
module HTTPStatus
##
# Root of the HTTP status class hierarchy
class Status < StandardError
class << self
attr_reader :code, :reason_phrase # :nodoc:
end
# Returns the HTTP status code
def code() self::class::code end
# Returns the HTTP status description
def reason_phrase() self::class::reason_phrase end
alias to_i code # :nodoc:
end
# Root of the HTTP info statuses
class Info < Status; end
# Root of the HTTP success statuses
class Success < Status; end
# Root of the HTTP redirect statuses
class Redirect < Status; end
# Root of the HTTP error statuses
class Error < Status; end
# Root of the HTTP client error statuses
class ClientError < Error; end
# Root of the HTTP server error statuses
class ServerError < Error; end
class EOFError < StandardError; end
# HTTP status codes and descriptions
StatusMessage = { # :nodoc:
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Large',
415 => 'Unsupported Media Type',
416 => 'Request Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
507 => 'Insufficient Storage',
511 => 'Network Authentication Required',
}
# Maps a status code to the corresponding Status class
CodeToError = {} # :nodoc:
# Creates a status or error class for each status code and
# populates the CodeToError map.
StatusMessage.each{|code, message|
message.freeze
var_name = message.gsub(/[ \-]/,'_').upcase
err_name = message.gsub(/[ \-]/,'')
case code
when 100...200; parent = Info
when 200...300; parent = Success
when 300...400; parent = Redirect
when 400...500; parent = ClientError
when 500...600; parent = ServerError
end
const_set("RC_#{var_name}", code)
err_class = Class.new(parent)
err_class.instance_variable_set(:@code, code)
err_class.instance_variable_set(:@reason_phrase, message)
const_set(err_name, err_class)
CodeToError[code] = err_class
}
##
# Returns the description corresponding to the HTTP status +code+
#
# WEBrick::HTTPStatus.reason_phrase 404
# => "Not Found"
def reason_phrase(code)
StatusMessage[code.to_i]
end
##
# Is +code+ an informational status?
def info?(code)
code.to_i >= 100 and code.to_i < 200
end
##
# Is +code+ a successful status?
def success?(code)
code.to_i >= 200 and code.to_i < 300
end
##
# Is +code+ a redirection status?
def redirect?(code)
code.to_i >= 300 and code.to_i < 400
end
##
# Is +code+ an error status?
def error?(code)
code.to_i >= 400 and code.to_i < 600
end
##
# Is +code+ a client error status?
def client_error?(code)
code.to_i >= 400 and code.to_i < 500
end
##
# Is +code+ a server error status?
def server_error?(code)
code.to_i >= 500 and code.to_i < 600
end
##
# Returns the status class corresponding to +code+
#
# WEBrick::HTTPStatus[302]
# => WEBrick::HTTPStatus::NotFound
#
def self.[](code)
CodeToError[code]
end
module_function :reason_phrase
module_function :info?, :success?, :redirect?, :error?
module_function :client_error?, :server_error?
end
end

View File

@ -1,523 +0,0 @@
# frozen_string_literal: true
#
# httputils.rb -- HTTPUtils Module
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $
require 'socket'
require 'tempfile'
module WEBrick
CR = "\x0d" # :nodoc:
LF = "\x0a" # :nodoc:
CRLF = "\x0d\x0a" # :nodoc:
##
# HTTPUtils provides utility methods for working with the HTTP protocol.
#
# This module is generally used internally by WEBrick
module HTTPUtils
##
# Normalizes a request path. Raises an exception if the path cannot be
# normalized.
def normalize_path(path)
raise "abnormal path `#{path}'" if path[0] != ?/
ret = path.dup
ret.gsub!(%r{/+}o, '/') # // => /
while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => /
while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo
raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
ret
end
module_function :normalize_path
##
# Default mime types
DefaultMimeTypes = {
"ai" => "application/postscript",
"asc" => "text/plain",
"avi" => "video/x-msvideo",
"avif" => "image/avif",
"bin" => "application/octet-stream",
"bmp" => "image/bmp",
"class" => "application/octet-stream",
"cer" => "application/pkix-cert",
"crl" => "application/pkix-crl",
"crt" => "application/x-x509-ca-cert",
#"crl" => "application/x-pkcs7-crl",
"css" => "text/css",
"dms" => "application/octet-stream",
"doc" => "application/msword",
"dvi" => "application/x-dvi",
"eps" => "application/postscript",
"etx" => "text/x-setext",
"exe" => "application/octet-stream",
"gif" => "image/gif",
"htm" => "text/html",
"html" => "text/html",
"ico" => "image/x-icon",
"jpe" => "image/jpeg",
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"js" => "application/javascript",
"json" => "application/json",
"lha" => "application/octet-stream",
"lzh" => "application/octet-stream",
"mjs" => "application/javascript",
"mov" => "video/quicktime",
"mp4" => "video/mp4",
"mpe" => "video/mpeg",
"mpeg" => "video/mpeg",
"mpg" => "video/mpeg",
"otf" => "font/otf",
"pbm" => "image/x-portable-bitmap",
"pdf" => "application/pdf",
"pgm" => "image/x-portable-graymap",
"png" => "image/png",
"pnm" => "image/x-portable-anymap",
"ppm" => "image/x-portable-pixmap",
"ppt" => "application/vnd.ms-powerpoint",
"ps" => "application/postscript",
"qt" => "video/quicktime",
"ras" => "image/x-cmu-raster",
"rb" => "text/plain",
"rd" => "text/plain",
"rtf" => "application/rtf",
"sgm" => "text/sgml",
"sgml" => "text/sgml",
"svg" => "image/svg+xml",
"tif" => "image/tiff",
"tiff" => "image/tiff",
"ttc" => "font/collection",
"ttf" => "font/ttf",
"txt" => "text/plain",
"wasm" => "application/wasm",
"webm" => "video/webm",
"webmanifest" => "application/manifest+json",
"webp" => "image/webp",
"woff" => "font/woff",
"woff2" => "font/woff2",
"xbm" => "image/x-xbitmap",
"xhtml" => "text/html",
"xls" => "application/vnd.ms-excel",
"xml" => "text/xml",
"xpm" => "image/x-xpixmap",
"xwd" => "image/x-xwindowdump",
"zip" => "application/zip",
}
##
# Loads Apache-compatible mime.types in +file+.
def load_mime_types(file)
# note: +file+ may be a "| command" for now; some people may
# rely on this, but currently we do not use this method by default.
File.open(file){ |io|
hash = Hash.new
io.each{ |line|
next if /^#/ =~ line
line.chomp!
mimetype, ext0 = line.split(/\s+/, 2)
next unless ext0
next if ext0.empty?
ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
}
hash
}
end
module_function :load_mime_types
##
# Returns the mime type of +filename+ from the list in +mime_tab+. If no
# mime type was found application/octet-stream is returned.
def mime_type(filename, mime_tab)
suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
end
module_function :mime_type
##
# Parses an HTTP header +raw+ into a hash of header fields with an Array
# of values.
def parse_header(raw)
header = Hash.new([].freeze)
field = nil
raw.each_line{|line|
case line
when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
field, value = $1, $2
field.downcase!
header[field] = [] unless header.has_key?(field)
header[field] << value
when /^\s+(.*?)\s*\z/om
value = $1
unless field
raise HTTPStatus::BadRequest, "bad header '#{line}'."
end
header[field][-1] << " " << value
else
raise HTTPStatus::BadRequest, "bad header '#{line}'."
end
}
header.each{|key, values|
values.each(&:strip!)
}
header
end
module_function :parse_header
##
# Splits a header value +str+ according to HTTP specification.
def split_header_value(str)
str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+)
(?:,\s*|\Z)'xn).flatten
end
module_function :split_header_value
##
# Parses a Range header value +ranges_specifier+
def parse_range_header(ranges_specifier)
if /^bytes=(.*)/ =~ ranges_specifier
byte_range_set = split_header_value($1)
byte_range_set.collect{|range_spec|
case range_spec
when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
when /^(\d+)-/ then $1.to_i .. -1
when /^-(\d+)/ then -($1.to_i) .. -1
else return nil
end
}
end
end
module_function :parse_range_header
##
# Parses q values in +value+ as used in Accept headers.
def parse_qvalues(value)
tmp = []
if value
parts = value.split(/,\s*/)
parts.each {|part|
if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
val = m[1]
q = (m[2] or 1).to_f
tmp.push([val, q])
end
}
tmp = tmp.sort_by{|val, q| -q}
tmp.collect!{|val, q| val}
end
return tmp
end
module_function :parse_qvalues
##
# Removes quotes and escapes from +str+
def dequote(str)
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
ret.gsub!(/\\(.)/, "\\1")
ret
end
module_function :dequote
##
# Quotes and escapes quotes in +str+
def quote(str)
+'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
end
module_function :quote
##
# Stores multipart form data. FormData objects are created when
# WEBrick::HTTPUtils.parse_form_data is called.
class FormData < String
EmptyRawHeader = [].freeze # :nodoc:
EmptyHeader = {}.freeze # :nodoc:
##
# The name of the form data part
attr_accessor :name
##
# The filename of the form data part
attr_accessor :filename
attr_accessor :next_data # :nodoc:
protected :next_data
##
# Creates a new FormData object.
#
# +args+ is an Array of form data entries. One FormData will be created
# for each entry.
#
# This is called by WEBrick::HTTPUtils.parse_form_data for you
def initialize(*args)
@name = @filename = @next_data = nil
if args.empty?
@raw_header = []
@header = nil
super("")
else
@raw_header = EmptyRawHeader
@header = EmptyHeader
super(args.shift)
unless args.empty?
@next_data = self.class.new(*args)
end
end
end
##
# Retrieves the header at the first entry in +key+
def [](*key)
begin
@header[key[0].downcase].join(", ")
rescue StandardError, NameError
super
end
end
##
# Adds +str+ to this FormData which may be the body, a header or a
# header entry.
#
# This is called by WEBrick::HTTPUtils.parse_form_data for you
def <<(str)
if @header
super
elsif str == CRLF
@header = HTTPUtils::parse_header(@raw_header.join)
if cd = self['content-disposition']
if /\s+name="(.*?)"/ =~ cd then @name = $1 end
if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
end
else
@raw_header << str
end
self
end
##
# Adds +data+ at the end of the chain of entries
#
# This is called by WEBrick::HTTPUtils.parse_form_data for you.
def append_data(data)
tmp = self
while tmp
unless tmp.next_data
tmp.next_data = data
break
end
tmp = tmp.next_data
end
self
end
##
# Yields each entry in this FormData
def each_data
tmp = self
while tmp
next_data = tmp.next_data
yield(tmp)
tmp = next_data
end
end
##
# Returns all the FormData as an Array
def list
ret = []
each_data{|data|
ret << data.to_s
}
ret
end
##
# A FormData will behave like an Array
alias :to_ary :list
##
# This FormData's body
def to_s
String.new(self)
end
end
##
# Parses the query component of a URI in +str+
def parse_query(str)
query = Hash.new
if str
str.split(/[&;]/).each{|x|
next if x.empty?
key, val = x.split(/=/,2)
key = unescape_form(key)
val = unescape_form(val.to_s)
val = FormData.new(val)
val.name = key
if query.has_key?(key)
query[key].append_data(val)
next
end
query[key] = val
}
end
query
end
module_function :parse_query
##
# Parses form data in +io+ with the given +boundary+
def parse_form_data(io, boundary)
boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
form_data = Hash.new
return form_data unless io
data = nil
io.each_line{|line|
if boundary_regexp =~ line
if data
data.chop!
key = data.name
if form_data.has_key?(key)
form_data[key].append_data(data)
else
form_data[key] = data
end
end
data = FormData.new
next
else
if data
data << line
end
end
}
return form_data
end
module_function :parse_form_data
#####
reserved = ';/?:@&=+$,'
num = '0123456789'
lowalpha = 'abcdefghijklmnopqrstuvwxyz'
upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
mark = '-_.!~*\'()'
unreserved = num + lowalpha + upalpha + mark
control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
space = " "
delims = '<>#%"'
unwise = '{}|\\^[]`'
nonascii = (0x80..0xff).collect{|c| c.chr }.join
module_function
# :stopdoc:
def _make_regex(str) /([#{Regexp.escape(str)}])/n end
def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
def _escape(str, regex)
str = str.b
str.gsub!(regex) {"%%%02X" % $1.ord}
# %-escaped string should contain US-ASCII only
str.force_encoding(Encoding::US_ASCII)
end
def _unescape(str, regex)
str = str.b
str.gsub!(regex) {$1.hex.chr}
# encoding of %-unescaped string is unknown
str
end
UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
NONASCII = _make_regex(nonascii)
ESCAPED = /%([0-9a-fA-F]{2})/
UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
# :startdoc:
##
# Escapes HTTP reserved and unwise characters in +str+
def escape(str)
_escape(str, UNESCAPED)
end
##
# Unescapes HTTP reserved and unwise characters in +str+
def unescape(str)
_unescape(str, ESCAPED)
end
##
# Escapes form reserved characters in +str+
def escape_form(str)
ret = _escape(str, UNESCAPED_FORM)
ret.gsub!(/ /, "+")
ret
end
##
# Unescapes form reserved characters in +str+
def unescape_form(str)
_unescape(str.gsub(/\+/, " "), ESCAPED)
end
##
# Escapes path +str+
def escape_path(str)
result = +""
str.scan(%r{/([^/]*)}).each{|i|
result << "/" << _escape(i[0], UNESCAPED_PCHAR)
}
return result
end
##
# Escapes 8 bit characters in +str+
def escape8bit(str)
_escape(str, NONASCII)
end
end
end

View File

@ -1,76 +0,0 @@
# frozen_string_literal: true
#--
# HTTPVersion.rb -- presentation of HTTP version
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $
module WEBrick
##
# Represents an HTTP protocol version
class HTTPVersion
include Comparable
##
# The major protocol version number
attr_accessor :major
##
# The minor protocol version number
attr_accessor :minor
##
# Converts +version+ into an HTTPVersion
def self.convert(version)
version.is_a?(self) ? version : new(version)
end
##
# Creates a new HTTPVersion from +version+.
def initialize(version)
case version
when HTTPVersion
@major, @minor = version.major, version.minor
when String
if /^(\d+)\.(\d+)$/ =~ version
@major, @minor = $1.to_i, $2.to_i
end
end
if @major.nil? || @minor.nil?
raise ArgumentError,
format("cannot convert %s into %s", version.class, self.class)
end
end
##
# Compares this version with +other+ according to the HTTP specification
# rules.
def <=>(other)
unless other.is_a?(self.class)
other = self.class.new(other)
end
if (ret = @major <=> other.major) == 0
return @minor <=> other.minor
end
return ret
end
##
# The HTTP version as show in the HTTP request and response. For example,
# "1.1"
def to_s
format("%d.%d", @major, @minor)
end
end
end

View File

@ -1,156 +0,0 @@
# frozen_string_literal: true
#--
# log.rb -- Log Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: log.rb,v 1.26 2002/10/06 17:06:10 gotoyuzo Exp $
module WEBrick
##
# A generic logging class
class BasicLog
# Fatal log level which indicates a server crash
FATAL = 1
# Error log level which indicates a recoverable error
ERROR = 2
# Warning log level which indicates a possible problem
WARN = 3
# Information log level which indicates possibly useful information
INFO = 4
# Debugging error level for messages used in server development or
# debugging
DEBUG = 5
# log-level, messages above this level will be logged
attr_accessor :level
##
# Initializes a new logger for +log_file+ that outputs messages at +level+
# or higher. +log_file+ can be a filename, an IO-like object that
# responds to #<< or nil which outputs to $stderr.
#
# If no level is given INFO is chosen by default
def initialize(log_file=nil, level=nil)
@level = level || INFO
case log_file
when String
@log = File.open(log_file, "a+")
@log.sync = true
@opened = true
when NilClass
@log = $stderr
else
@log = log_file # requires "<<". (see BasicLog#log)
end
end
##
# Closes the logger (also closes the log device associated to the logger)
def close
@log.close if @opened
@log = nil
end
##
# Logs +data+ at +level+ if the given level is above the current log
# level.
def log(level, data)
if @log && level <= @level
data += "\n" if /\n\Z/ !~ data
@log << data
end
end
##
# Synonym for log(INFO, obj.to_s)
def <<(obj)
log(INFO, obj.to_s)
end
# Shortcut for logging a FATAL message
def fatal(msg) log(FATAL, "FATAL " + format(msg)); end
# Shortcut for logging an ERROR message
def error(msg) log(ERROR, "ERROR " + format(msg)); end
# Shortcut for logging a WARN message
def warn(msg) log(WARN, "WARN " + format(msg)); end
# Shortcut for logging an INFO message
def info(msg) log(INFO, "INFO " + format(msg)); end
# Shortcut for logging a DEBUG message
def debug(msg) log(DEBUG, "DEBUG " + format(msg)); end
# Will the logger output FATAL messages?
def fatal?; @level >= FATAL; end
# Will the logger output ERROR messages?
def error?; @level >= ERROR; end
# Will the logger output WARN messages?
def warn?; @level >= WARN; end
# Will the logger output INFO messages?
def info?; @level >= INFO; end
# Will the logger output DEBUG messages?
def debug?; @level >= DEBUG; end
private
##
# Formats +arg+ for the logger
#
# * If +arg+ is an Exception, it will format the error message and
# the back trace.
# * If +arg+ responds to #to_str, it will return it.
# * Otherwise it will return +arg+.inspect.
def format(arg)
if arg.is_a?(Exception)
+"#{arg.class}: #{AccessLog.escape(arg.message)}\n\t" <<
arg.backtrace.join("\n\t") << "\n"
elsif arg.respond_to?(:to_str)
AccessLog.escape(arg.to_str)
else
arg.inspect
end
end
end
##
# A logging class that prepends a timestamp to each message.
class Log < BasicLog
# Format of the timestamp which is applied to each logged line. The
# default is <tt>"[%Y-%m-%d %H:%M:%S]"</tt>
attr_accessor :time_format
##
# Same as BasicLog#initialize
#
# You can set the timestamp format through #time_format
def initialize(log_file=nil, level=nil)
super(log_file, level)
@time_format = "[%Y-%m-%d %H:%M:%S]"
end
##
# Same as BasicLog#log
def log(level, data)
tmp = Time.now.strftime(@time_format)
tmp << " " << data
super(level, tmp)
end
end
end

View File

@ -1,381 +0,0 @@
# frozen_string_literal: true
#
# server.rb -- GenericServer Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
require 'socket'
require_relative 'config'
require_relative 'log'
module WEBrick
##
# Server error exception
class ServerError < StandardError; end
##
# Base server class
class SimpleServer
##
# A SimpleServer only yields when you start it
def SimpleServer.start
yield
end
end
##
# A generic module for daemonizing a process
class Daemon
##
# Performs the standard operations for daemonizing a process. Runs a
# block, if given.
def Daemon.start
Process.daemon
File.umask(0)
yield if block_given?
end
end
##
# Base TCP server class. You must subclass GenericServer and provide a #run
# method.
class GenericServer
##
# The server status. One of :Stop, :Running or :Shutdown
attr_reader :status
##
# The server configuration
attr_reader :config
##
# The server logger. This is independent from the HTTP access log.
attr_reader :logger
##
# Tokens control the number of outstanding clients. The
# <code>:MaxClients</code> configuration sets this.
attr_reader :tokens
##
# Sockets listening for connections.
attr_reader :listeners
##
# Creates a new generic server from +config+. The default configuration
# comes from +default+.
def initialize(config={}, default=Config::General)
@config = default.dup.update(config)
@status = :Stop
@config[:Logger] ||= Log::new
@logger = @config[:Logger]
@tokens = Thread::SizedQueue.new(@config[:MaxClients])
@config[:MaxClients].times{ @tokens.push(nil) }
webrickv = WEBrick::VERSION
rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
@logger.info("WEBrick #{webrickv}")
@logger.info("ruby #{rubyv}")
@listeners = []
@shutdown_pipe = nil
unless @config[:DoNotListen]
raise ArgumentError, "Port must be an integer" unless @config[:Port].to_s == @config[:Port].to_i.to_s
@config[:Port] = @config[:Port].to_i
if @config[:Listen]
warn(":Listen option is deprecated; use GenericServer#listen", uplevel: 1)
end
listen(@config[:BindAddress], @config[:Port])
if @config[:Port] == 0
@config[:Port] = @listeners[0].addr[1]
end
end
end
##
# Retrieves +key+ from the configuration
def [](key)
@config[key]
end
##
# Adds listeners from +address+ and +port+ to the server. See
# WEBrick::Utils::create_listeners for details.
def listen(address, port)
@listeners += Utils::create_listeners(address, port)
end
##
# Starts the server and runs the +block+ for each connection. This method
# does not return until the server is stopped from a signal handler or
# another thread using #stop or #shutdown.
#
# If the block raises a subclass of StandardError the exception is logged
# and ignored. If an IOError or Errno::EBADF exception is raised the
# exception is ignored. If an Exception subclass is raised the exception
# is logged and re-raised which stops the server.
#
# To completely shut down a server call #shutdown from ensure:
#
# server = WEBrick::GenericServer.new
# # or WEBrick::HTTPServer.new
#
# begin
# server.start
# ensure
# server.shutdown
# end
def start(&block)
raise ServerError, "already started." if @status != :Stop
server_type = @config[:ServerType] || SimpleServer
setup_shutdown_pipe
server_type.start{
@logger.info \
"#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
@status = :Running
call_callback(:StartCallback)
shutdown_pipe = @shutdown_pipe
thgroup = ThreadGroup.new
begin
while @status == :Running
begin
sp = shutdown_pipe[0]
if svrs = IO.select([sp, *@listeners])
if svrs[0].include? sp
# swallow shutdown pipe
buf = String.new
nil while String ===
sp.read_nonblock([sp.nread, 8].max, buf, exception: false)
break
end
svrs[0].each{|svr|
@tokens.pop # blocks while no token is there.
if sock = accept_client(svr)
unless config[:DoNotReverseLookup].nil?
sock.do_not_reverse_lookup = !!config[:DoNotReverseLookup]
end
th = start_thread(sock, &block)
th[:WEBrickThread] = true
thgroup.add(th)
else
@tokens.push(nil)
end
}
end
rescue Errno::EBADF, Errno::ENOTSOCK, IOError => ex
# if the listening socket was closed in GenericServer#shutdown,
# IO::select raise it.
rescue StandardError => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
rescue Exception => ex
@logger.fatal ex
raise
end
end
ensure
cleanup_shutdown_pipe(shutdown_pipe)
cleanup_listener
@status = :Shutdown
@logger.info "going to shutdown ..."
thgroup.list.each{|th| th.join if th[:WEBrickThread] }
call_callback(:StopCallback)
@logger.info "#{self.class}#start done."
@status = :Stop
end
}
end
##
# Stops the server from accepting new connections.
def stop
if @status == :Running
@status = :Shutdown
end
alarm_shutdown_pipe {|f| f.write_nonblock("\0")}
end
##
# Shuts down the server and all listening sockets. New listeners must be
# provided to restart the server.
def shutdown
stop
alarm_shutdown_pipe(&:close)
end
##
# You must subclass GenericServer and implement \#run which accepts a TCP
# client socket
def run(sock)
@logger.fatal "run() must be provided by user."
end
private
# :stopdoc:
##
# Accepts a TCP client socket from the TCP server socket +svr+ and returns
# the client socket.
def accept_client(svr)
case sock = svr.to_io.accept_nonblock(exception: false)
when :wait_readable
nil
else
if svr.respond_to?(:start_immediately)
sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
sock.sync_close = true
# we cannot do OpenSSL::SSL::SSLSocket#accept here because
# a slow client can prevent us from accepting connections
# from other clients
end
sock
end
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
Errno::EPROTO, Errno::EINVAL
nil
rescue StandardError => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
nil
end
##
# Starts a server thread for the client socket +sock+ that runs the given
# +block+.
#
# Sets the socket to the <code>:WEBrickSocket</code> thread local variable
# in the thread.
#
# If any errors occur in the block they are logged and handled.
def start_thread(sock, &block)
Thread.start{
begin
Thread.current[:WEBrickSocket] = sock
begin
addr = sock.peeraddr
@logger.debug "accept: #{addr[3]}:#{addr[1]}"
rescue SocketError
@logger.debug "accept: <address unknown>"
raise
end
if sock.respond_to?(:sync_close=) && @config[:SSLStartImmediately]
WEBrick::Utils.timeout(@config[:RequestTimeout]) do
begin
sock.accept # OpenSSL::SSL::SSLSocket#accept
rescue Errno::ECONNRESET, Errno::ECONNABORTED,
Errno::EPROTO, Errno::EINVAL
Thread.exit
end
end
end
call_callback(:AcceptCallback, sock)
block ? block.call(sock) : run(sock)
rescue Errno::ENOTCONN
@logger.debug "Errno::ENOTCONN raised"
rescue ServerError => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
rescue Exception => ex
@logger.error ex
ensure
@tokens.push(nil)
Thread.current[:WEBrickSocket] = nil
if addr
@logger.debug "close: #{addr[3]}:#{addr[1]}"
else
@logger.debug "close: <address unknown>"
end
sock.close
end
}
end
##
# Calls the callback +callback_name+ from the configuration with +args+
def call_callback(callback_name, *args)
@config[callback_name]&.call(*args)
end
def setup_shutdown_pipe
return @shutdown_pipe ||= IO.pipe
end
def cleanup_shutdown_pipe(shutdown_pipe)
@shutdown_pipe = nil
shutdown_pipe&.each(&:close)
end
def alarm_shutdown_pipe
_, pipe = @shutdown_pipe # another thread may modify @shutdown_pipe.
if pipe
if !pipe.closed?
begin
yield pipe
rescue IOError # closed by another thread.
end
end
end
end
def cleanup_listener
@listeners.each{|s|
if @logger.debug?
addr = s.addr
@logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
end
begin
s.shutdown
rescue Errno::ENOTCONN
# when `Errno::ENOTCONN: Socket is not connected' on some platforms,
# call #close instead of #shutdown.
# (ignore @config[:ShutdownSocketWithoutClose])
s.close
else
unless @config[:ShutdownSocketWithoutClose]
s.close
end
end
}
@listeners.clear
end
end # end of GenericServer
end

View File

@ -1,219 +0,0 @@
# frozen_string_literal: true
#
# ssl.rb -- SSL/TLS enhancement for GenericServer
#
# Copyright (c) 2003 GOTOU Yuuzou All rights reserved.
#
# $Id$
require 'webrick'
require 'openssl'
module WEBrick
module Config
svrsoft = General[:ServerSoftware]
osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
##
# Default SSL server configuration.
#
# WEBrick can automatically create a self-signed certificate if
# <code>:SSLCertName</code> is set. For more information on the various
# SSL options see OpenSSL::SSL::SSLContext.
#
# :ServerSoftware ::
# The server software name used in the Server: header.
# :SSLEnable :: false,
# Enable SSL for this server. Defaults to false.
# :SSLCertificate ::
# The SSL certificate for the server.
# :SSLPrivateKey ::
# The SSL private key for the server certificate.
# :SSLClientCA :: nil,
# Array of certificates that will be sent to the client.
# :SSLExtraChainCert :: nil,
# Array of certificates that will be added to the certificate chain
# :SSLCACertificateFile :: nil,
# Path to a CA certificate file
# :SSLCACertificatePath :: nil,
# Path to a directory containing CA certificates
# :SSLCertificateStore :: nil,
# OpenSSL::X509::Store used for certificate validation of the client
# :SSLTmpDhCallback :: nil,
# Callback invoked when DH parameters are required.
# :SSLVerifyClient ::
# Sets whether the client is verified. This defaults to VERIFY_NONE
# which is typical for an HTTPS server.
# :SSLVerifyDepth ::
# Number of CA certificates to walk when verifying a certificate chain
# :SSLVerifyCallback ::
# Custom certificate verification callback
# :SSLServerNameCallback::
# Custom servername indication callback
# :SSLTimeout ::
# Maximum session lifetime
# :SSLOptions ::
# Various SSL options
# :SSLCiphers ::
# Ciphers to be used
# :SSLStartImmediately ::
# Immediately start SSL upon connection? Defaults to true
# :SSLCertName ::
# SSL certificate name. Must be set to enable automatic certificate
# creation.
# :SSLCertComment ::
# Comment used during automatic certificate creation.
SSL = {
:ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}",
:SSLEnable => false,
:SSLCertificate => nil,
:SSLPrivateKey => nil,
:SSLClientCA => nil,
:SSLExtraChainCert => nil,
:SSLCACertificateFile => nil,
:SSLCACertificatePath => nil,
:SSLCertificateStore => nil,
:SSLTmpDhCallback => nil,
:SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
:SSLVerifyDepth => nil,
:SSLVerifyCallback => nil, # custom verification
:SSLTimeout => nil,
:SSLOptions => nil,
:SSLCiphers => nil,
:SSLStartImmediately => true,
# Must specify if you use auto generated certificate.
:SSLCertName => nil,
:SSLCertComment => "Generated by Ruby/OpenSSL"
}
General.update(SSL)
end
module Utils
##
# Creates a self-signed certificate with the given number of +bits+,
# the issuer +cn+ and a +comment+ to be stored in the certificate.
def create_self_signed_cert(bits, cn, comment)
rsa = if $VERBOSE
OpenSSL::PKey::RSA.new(bits){|p, n|
case p
when 0; $stderr.putc "." # BN_generate_prime
when 1; $stderr.putc "+" # BN_generate_prime
when 2; $stderr.putc "*" # searching good prime,
# n = #of try,
# but also data from BN_generate_prime
when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
# but also data from BN_generate_prime
else; $stderr.putc "*" # BN_generate_prime
end
}
else
OpenSSL::PKey::RSA.new(bits)
end
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn)
: OpenSSL::X509::Name.new(cn)
cert.subject = name
cert.issuer = name
cert.not_before = Time.now
cert.not_after = Time.now + (365*24*60*60)
cert.public_key = rsa.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE"),
ef.create_extension("keyUsage", "keyEncipherment, digitalSignature, keyAgreement, dataEncipherment"),
ef.create_extension("subjectKeyIdentifier", "hash"),
ef.create_extension("extendedKeyUsage", "serverAuth"),
ef.create_extension("nsComment", comment),
]
aki = ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.add_extension(aki)
cert.sign(rsa, "SHA256")
return [ cert, rsa ]
end
module_function :create_self_signed_cert
end
##
#--
# Updates WEBrick::GenericServer with SSL functionality
class GenericServer
##
# SSL context for the server when run in SSL mode
def ssl_context # :nodoc:
@ssl_context ||= begin
if @config[:SSLEnable]
ssl_context = setup_ssl_context(@config)
@logger.info("\n" + @config[:SSLCertificate].to_text)
ssl_context
end
end
end
undef listen
##
# Updates +listen+ to enable SSL when the SSL configuration is active.
def listen(address, port) # :nodoc:
listeners = Utils::create_listeners(address, port)
if @config[:SSLEnable]
listeners.collect!{|svr|
ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
ssvr.start_immediately = @config[:SSLStartImmediately]
ssvr
}
end
@listeners += listeners
setup_shutdown_pipe
end
##
# Sets up an SSL context for +config+
def setup_ssl_context(config) # :nodoc:
unless config[:SSLCertificate]
cn = config[:SSLCertName]
comment = config[:SSLCertComment]
cert, key = Utils::create_self_signed_cert(2048, cn, comment)
config[:SSLCertificate] = cert
config[:SSLPrivateKey] = key
end
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = config[:SSLPrivateKey]
ctx.cert = config[:SSLCertificate]
ctx.client_ca = config[:SSLClientCA]
ctx.extra_chain_cert = config[:SSLExtraChainCert]
ctx.ca_file = config[:SSLCACertificateFile]
ctx.ca_path = config[:SSLCACertificatePath]
ctx.cert_store = config[:SSLCertificateStore]
ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
ctx.verify_mode = config[:SSLVerifyClient]
ctx.verify_depth = config[:SSLVerifyDepth]
ctx.verify_callback = config[:SSLVerifyCallback]
ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
ctx.timeout = config[:SSLTimeout]
ctx.options = config[:SSLOptions]
ctx.ciphers = config[:SSLCiphers]
ctx
end
##
# ServerNameIndication callback
def ssl_servername_callback(sslsocket, hostname = nil)
# default
end
end
end

View File

@ -1,265 +0,0 @@
# frozen_string_literal: true
#
# utils.rb -- Miscellaneous utilities
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
require 'socket'
require 'io/nonblock'
require 'etc'
module WEBrick
module Utils
##
# Sets IO operations on +io+ to be non-blocking
def set_non_blocking(io)
io.nonblock = true if io.respond_to?(:nonblock=)
end
module_function :set_non_blocking
##
# Sets the close on exec flag for +io+
def set_close_on_exec(io)
io.close_on_exec = true if io.respond_to?(:close_on_exec=)
end
module_function :set_close_on_exec
##
# Changes the process's uid and gid to the ones of +user+
def su(user)
if pw = Etc.getpwnam(user)
Process::initgroups(user, pw.gid)
Process::Sys::setgid(pw.gid)
Process::Sys::setuid(pw.uid)
else
warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1)
end
end
module_function :su
##
# The server hostname
def getservername
Socket::gethostname
end
module_function :getservername
##
# Creates TCP server sockets bound to +address+:+port+ and returns them.
#
# It will create IPV4 and IPV6 sockets on all interfaces.
def create_listeners(address, port)
unless port
raise ArgumentError, "must specify port"
end
sockets = Socket.tcp_server_sockets(address, port)
sockets = sockets.map {|s|
s.autoclose = false
ts = TCPServer.for_fd(s.fileno)
s.close
ts
}
return sockets
end
module_function :create_listeners
##
# Characters used to generate random strings
RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789" +
"abcdefghijklmnopqrstuvwxyz"
##
# Generates a random string of length +len+
def random_string(len)
rand_max = RAND_CHARS.bytesize
ret = +""
len.times{ ret << RAND_CHARS[rand(rand_max)] }
ret
end
module_function :random_string
###########
require "timeout"
require "singleton"
##
# Class used to manage timeout handlers across multiple threads.
#
# Timeout handlers should be managed by using the class methods which are
# synchronized.
#
# id = TimeoutHandler.register(10, Timeout::Error)
# begin
# sleep 20
# puts 'foo'
# ensure
# TimeoutHandler.cancel(id)
# end
#
# will raise Timeout::Error
#
# id = TimeoutHandler.register(10, Timeout::Error)
# begin
# sleep 5
# puts 'foo'
# ensure
# TimeoutHandler.cancel(id)
# end
#
# will print 'foo'
#
class TimeoutHandler
include Singleton
##
# Mutex used to synchronize access across threads
TimeoutMutex = Thread::Mutex.new # :nodoc:
##
# Registers a new timeout handler
#
# +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed
def TimeoutHandler.register(seconds, exception)
at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
instance.register(Thread.current, at, exception)
end
##
# Cancels the timeout handler +id+
def TimeoutHandler.cancel(id)
instance.cancel(Thread.current, id)
end
def self.terminate
instance.terminate
end
##
# Creates a new TimeoutHandler. You should use ::register and ::cancel
# instead of creating the timeout handler directly.
def initialize
TimeoutMutex.synchronize{
@timeout_info = Hash.new
}
@queue = Thread::Queue.new
@watcher = nil
end
# :nodoc:
private \
def watch
to_interrupt = []
while true
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
wakeup = nil
to_interrupt.clear
TimeoutMutex.synchronize{
@timeout_info.each {|thread, ary|
next unless ary
ary.each{|info|
time, exception = *info
if time < now
to_interrupt.push [thread, info.object_id, exception]
elsif !wakeup || time < wakeup
wakeup = time
end
}
}
}
to_interrupt.each {|arg| interrupt(*arg)}
if !wakeup
@queue.pop
elsif (wakeup -= now) > 0
begin
(th = Thread.start {@queue.pop}).join(wakeup)
ensure
th&.kill&.join
end
end
@queue.clear
end
end
# :nodoc:
private \
def watcher
(w = @watcher)&.alive? and return w # usual case
TimeoutMutex.synchronize{
(w = @watcher)&.alive? and next w # pathological check
@watcher = Thread.start(&method(:watch))
}
end
##
# Interrupts the timeout handler +id+ and raises +exception+
def interrupt(thread, id, exception)
if cancel(thread, id) && thread.alive?
thread.raise(exception, "execution timeout")
end
end
##
# Registers a new timeout handler
#
# +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed
def register(thread, time, exception)
info = nil
TimeoutMutex.synchronize{
(@timeout_info[thread] ||= []) << (info = [time, exception])
}
@queue.push nil
watcher
return info.object_id
end
##
# Cancels the timeout handler +id+
def cancel(thread, id)
TimeoutMutex.synchronize{
if ary = @timeout_info[thread]
ary.delete_if{|info| info.object_id == id }
if ary.empty?
@timeout_info.delete(thread)
end
return true
end
return false
}
end
##
def terminate
TimeoutMutex.synchronize{
@timeout_info.clear
@watcher&.kill&.join
}
end
end
##
# Executes the passed block and raises +exception+ if execution takes more
# than +seconds+.
#
# If +seconds+ is zero or nil, simply executes the block
def timeout(seconds, exception=Timeout::Error)
return yield if seconds.nil? or seconds.zero?
# raise ThreadError, "timeout within critical session" if Thread.critical
id = TimeoutHandler.register(seconds, exception)
begin
yield(seconds)
ensure
TimeoutHandler.cancel(id)
end
end
module_function :timeout
end
end

View File

@ -1,18 +0,0 @@
# frozen_string_literal: true
#--
# version.rb -- version and release date
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $
module WEBrick
##
# The WEBrick version
VERSION = "1.8.1"
end

1
Library/Homebrew/vendor/gems/mechanize vendored Symbolic link
View File

@ -0,0 +1 @@
mechanize-2.9.1/

View File

@ -0,0 +1,24 @@
(The MIT License)
Copyright (c) 2005 by Michael Neumann (mneumann@ntecs.de)
Copyright (c) 2006-2021 by Eric Hodel, Akinori MUSHA, Aaron Patterson, Lee Jarvis, Mike Dalessio
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.