1=begin 2= Ruby-space definitions that completes C-space funcs for Config 3 4= Info 5 Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org> 6 7= Licence 8 This program is licenced under the same licence as Ruby. 9 (See the file 'LICENCE'.) 10 11=end 12 13require 'stringio' 14 15module OpenSSL 16 class Config 17 include Enumerable 18 19 class << self 20 def parse(str) 21 c = new() 22 parse_config(StringIO.new(str)).each do |section, hash| 23 c[section] = hash 24 end 25 c 26 end 27 28 alias load new 29 30 def parse_config(io) 31 begin 32 parse_config_lines(io) 33 rescue ConfigError => e 34 e.message.replace("error in line #{io.lineno}: " + e.message) 35 raise 36 end 37 end 38 39 def get_key_string(data, section, key) # :nodoc: 40 if v = data[section] && data[section][key] 41 return v 42 elsif section == 'ENV' 43 if v = ENV[key] 44 return v 45 end 46 end 47 if v = data['default'] && data['default'][key] 48 return v 49 end 50 end 51 52 private 53 54 def parse_config_lines(io) 55 section = 'default' 56 data = {section => {}} 57 while definition = get_definition(io) 58 definition = clear_comments(definition) 59 next if definition.empty? 60 if definition[0] == ?[ 61 if /\[([^\]]*)\]/ =~ definition 62 section = $1.strip 63 data[section] ||= {} 64 else 65 raise ConfigError, "missing close square bracket" 66 end 67 else 68 if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition 69 if $2 70 section = $1 71 key = $2 72 else 73 key = $1 74 end 75 value = unescape_value(data, section, $3) 76 (data[section] ||= {})[key] = value.strip 77 else 78 raise ConfigError, "missing equal sign" 79 end 80 end 81 end 82 data 83 end 84 85 # escape with backslash 86 QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/ 87 # escape with backslash and doubled dq 88 QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/ 89 # escaped char map 90 ESCAPE_MAP = { 91 "r" => "\r", 92 "n" => "\n", 93 "b" => "\b", 94 "t" => "\t", 95 } 96 97 def unescape_value(data, section, value) 98 scanned = [] 99 while m = value.match(/['"\\$]/) 100 scanned << m.pre_match 101 c = m[0] 102 value = m.post_match 103 case c 104 when "'" 105 if m = value.match(QUOTE_REGEXP_SQ) 106 scanned << m[1].gsub(/\\(.)/, '\\1') 107 value = m.post_match 108 else 109 break 110 end 111 when '"' 112 if m = value.match(QUOTE_REGEXP_DQ) 113 scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1') 114 value = m.post_match 115 else 116 break 117 end 118 when "\\" 119 c = value.slice!(0, 1) 120 scanned << (ESCAPE_MAP[c] || c) 121 when "$" 122 ref, value = extract_reference(value) 123 refsec = section 124 if ref.index('::') 125 refsec, ref = ref.split('::', 2) 126 end 127 if v = get_key_string(data, refsec, ref) 128 scanned << v 129 else 130 raise ConfigError, "variable has no value" 131 end 132 else 133 raise 'must not reaced' 134 end 135 end 136 scanned << value 137 scanned.join 138 end 139 140 def extract_reference(value) 141 rest = '' 142 if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/) 143 value = m[1] || m[2] 144 rest = m.post_match 145 elsif [?(, ?{].include?(value[0]) 146 raise ConfigError, "no close brace" 147 end 148 if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/) 149 return m[0], m.post_match + rest 150 else 151 raise 152 end 153 end 154 155 def clear_comments(line) 156 # FCOMMENT 157 if m = line.match(/\A([\t\n\f ]*);.*\z/) 158 return m[1] 159 end 160 # COMMENT 161 scanned = [] 162 while m = line.match(/[#'"\\]/) 163 scanned << m.pre_match 164 c = m[0] 165 line = m.post_match 166 case c 167 when '#' 168 line = nil 169 break 170 when "'", '"' 171 regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ 172 scanned << c 173 if m = line.match(regexp) 174 scanned << m[0] 175 line = m.post_match 176 else 177 scanned << line 178 line = nil 179 break 180 end 181 when "\\" 182 scanned << c 183 scanned << line.slice!(0, 1) 184 else 185 raise 'must not reaced' 186 end 187 end 188 scanned << line 189 scanned.join 190 end 191 192 def get_definition(io) 193 if line = get_line(io) 194 while /[^\\]\\\z/ =~ line 195 if extra = get_line(io) 196 line += extra 197 else 198 break 199 end 200 end 201 return line.strip 202 end 203 end 204 205 def get_line(io) 206 if line = io.gets 207 line.gsub(/[\r\n]*/, '') 208 end 209 end 210 end 211 212 def initialize(filename = nil) 213 @data = {} 214 if filename 215 File.open(filename.to_s) do |file| 216 Config.parse_config(file).each do |section, hash| 217 self[section] = hash 218 end 219 end 220 end 221 end 222 223 def get_value(section, key) 224 if section.nil? 225 raise TypeError.new('nil not allowed') 226 end 227 section = 'default' if section.empty? 228 get_key_string(section, key) 229 end 230 231 def value(arg1, arg2 = nil) 232 warn('Config#value is deprecated; use Config#get_value') 233 if arg2.nil? 234 section, key = 'default', arg1 235 else 236 section, key = arg1, arg2 237 end 238 section ||= 'default' 239 section = 'default' if section.empty? 240 get_key_string(section, key) 241 end 242 243 def add_value(section, key, value) 244 check_modify 245 (@data[section] ||= {})[key] = value 246 end 247 248 def [](section) 249 @data[section] || {} 250 end 251 252 def section(name) 253 warn('Config#section is deprecated; use Config#[]') 254 @data[name] || {} 255 end 256 257 def []=(section, pairs) 258 check_modify 259 @data[section] ||= {} 260 pairs.each do |key, value| 261 self.add_value(section, key, value) 262 end 263 end 264 265 def sections 266 @data.keys 267 end 268 269 def to_s 270 ary = [] 271 @data.keys.sort.each do |section| 272 ary << "[ #{section} ]\n" 273 @data[section].keys.each do |key| 274 ary << "#{key}=#{@data[section][key]}\n" 275 end 276 ary << "\n" 277 end 278 ary.join 279 end 280 281 def each 282 @data.each do |section, hash| 283 hash.each do |key, value| 284 yield [section, key, value] 285 end 286 end 287 end 288 289 def inspect 290 "#<#{self.class.name} sections=#{sections.inspect}>" 291 end 292 293 protected 294 295 def data 296 @data 297 end 298 299 private 300 301 def initialize_copy(other) 302 @data = other.data.dup 303 end 304 305 def check_modify 306 raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen? 307 end 308 309 def get_key_string(section, key) 310 Config.get_key_string(@data, section, key) 311 end 312 end 313end 314