# frozen_string_literal: false
= Ruby-space definitions that completes C-space funcs for Config
Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
This program is licensed under the same licence as Ruby.
(See the file 'LICENCE'.)
# Configuration for the openssl library.
# Many system's installation of openssl library will depend on your system
# configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
# the location of the file for your host.
# See also http://www.openssl.org/docs/apps/config.html
# Parses a given _string_ as a blob that contains configuration for
# If the source of the IO is a file, then consider using #parse_config.
parse_config(StringIO.new(string)).each do |section, hash|
# load is an alias to ::new
# Parses the configuration data read from _io_, see also #parse.
# Raises a ConfigError on invalid configuration data.
e.message.replace("error in line #{io.lineno}: " + e.message)
def get_key_string(data, section, key) # :nodoc:
if v = data[section] && data[section][key]
if v = data['default'] && data['default'][key]
def parse_config_lines(io)
while definition = get_definition(io_stack)
definition = clear_comments(definition)
next if definition.empty?
if /\[([^\]]*)\]/ =~ definition
raise ConfigError, "missing close square bracket"
when /\A\.include (.+)\z/
files = Dir.glob(File.join(path, "*.{cnf,conf}"), File::FNM_EXTGLOB)
io_stack << StringIO.new(File.read(filename))
raise ConfigError, "could not include file '%s'" % filename
when /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/
value = unescape_value(data, section, $3)
(data[section] ||= {})[key] = value.strip
raise ConfigError, "missing equal sign"
QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
# escape with backslash and doubled dq
QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
def unescape_value(data, section, value)
while m = value.match(/['"\\$]/)
if m = value.match(QUOTE_REGEXP_SQ)
scanned << m[1].gsub(/\\(.)/, '\\1')
if m = value.match(QUOTE_REGEXP_DQ)
scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
scanned << (ESCAPE_MAP[c] || c)
ref, value = extract_reference(value)
refsec, ref = ref.split('::', 2)
if v = get_key_string(data, refsec, ref)
raise ConfigError, "variable has no value"
def extract_reference(value)
if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
elsif [?(, ?{].include?(value[0])
raise ConfigError, "no close brace"
if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
return m[0], m.post_match + rest
if m = line.match(/\A([\t\n\f ]*);.*\z/)
while m = line.match(/[#'"\\]/)
regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
if m = line.match(regexp)
scanned << line.slice!(0, 1)
def get_definition(io_stack)
if line = get_line(io_stack)
while /[^\\]\\\z/ =~ line
if extra = get_line(io_stack)
return line.gsub(/[\r\n]*/, '')
# Creates an instance of OpenSSL's configuration class.
# This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
# If the optional _filename_ parameter is provided, then it is read in and
# parsed via #parse_config.
# This can raise IO exceptions based on the access, or availability of the
# file. A ConfigError exception may be raised depending on the validity of
# the data being configured.
def initialize(filename = nil)
File.open(filename.to_s) do |file|
Config.parse_config(file).each do |section, hash|
# Gets the value of _key_ from the given _section_
# Given the following configurating file being loaded:
# config = OpenSSL::Config.load('foo.cnf')
# #=> #<OpenSSL::Config sections=["default"]>
# You can get a specific value from the config if you know the _section_
# config.get_value('default','foo')
def get_value(section, key)
raise TypeError.new('nil not allowed')
section = 'default' if section.empty?
get_key_string(section, key)
def value(arg1, arg2 = nil) # :nodoc:
warn('Config#value is deprecated; use Config#get_value')
section, key = 'default', arg1
section, key = arg1, arg2
section = 'default' if section.empty?
get_key_string(section, key)
# Set the target _key_ with a given _value_ under a specific _section_.
# Given the following configurating file being loaded:
# config = OpenSSL::Config.load('foo.cnf')
# #=> #<OpenSSL::Config sections=["default"]>
# You can set the value of _foo_ under the _default_ section to a new
# config.add_value('default', 'foo', 'buzz')
def add_value(section, key, value)
(@data[section] ||= {})[key] = value
# Get a specific _section_ from the current configuration
# Given the following configurating file being loaded:
# config = OpenSSL::Config.load('foo.cnf')
# #=> #<OpenSSL::Config sections=["default"]>
# You can get a hash of the specific section like so:
def section(name) # :nodoc:
warn('Config#section is deprecated; use Config#[]')
# Sets a specific _section_ name with a Hash _pairs_.
# Given the following configuration being created:
# config = OpenSSL::Config.new
# #=> #<OpenSSL::Config sections=[]>
# config['default'] = {"foo"=>"bar","baz"=>"buz"}
# #=> {"foo"=>"bar", "baz"=>"buz"}
# It's important to note that this will essentially merge any of the keys
# in _pairs_ with the existing _section_. For example:
# #=> {"foo"=>"bar", "baz"=>"buz"}
# config['default'] = {"foo" => "changed"}
# #=> {"foo"=>"changed", "baz"=>"buz"}
pairs.each do |key, value|
self.add_value(section, key, value)
# Get the names of all sections in the current configuration
# Get the parsable form of the current configuration
# Given the following configuration being created:
# config = OpenSSL::Config.new
# #=> #<OpenSSL::Config sections=[]>
# config['default'] = {"foo"=>"bar","baz"=>"buz"}
# #=> {"foo"=>"bar", "baz"=>"buz"}
# You can parse get the serialized configuration using #to_s and then parse
# serialized_config = config.to_s
# new_config = OpenSSL::Config.parse(serialized_config)
# #=> #<OpenSSL::Config sections=["default"]>
@data.keys.sort.each do |section|
ary << "[ #{section} ]\n"
@data[section].keys.each do |key|
ary << "#{key}=#{@data[section][key]}\n"
# Receive the section and its pairs for the current configuration.
# config.each do |section, key, value|
@data.each do |section, hash|
hash.each do |key, value|
yield [section, key, value]
# String representation of this configuration object, including the class
"#<#{self.class.name} sections=#{sections.inspect}>"
def initialize_copy(other)
raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
def get_key_string(section, key)
Config.get_key_string(@data, section, key)