1
1
# -*- coding: binary -*-
2
+
3
+ require 'set'
4
+
2
5
module Msf
3
6
4
7
###
@@ -13,17 +16,19 @@ class DataStore < Hash
13
16
# Initializes the data store's internal state.
14
17
#
15
18
def initialize ( )
16
- @options = Hash . new
17
- @imported = Hash . new
18
- @imported_by = Hash . new
19
+ @options = Hash . new
20
+ @imported = Hash . new
21
+ @imported_by = Hash . new
22
+ @original_keys = Set . new
19
23
end
20
24
21
25
#
22
26
# Clears the imported flag for the supplied key since it's being set
23
27
# directly.
24
28
#
25
29
def []=( k , v )
26
- k = find_key_case ( k )
30
+ add_key ( k )
31
+ k = k . downcase
27
32
@imported [ k ] = false
28
33
@imported_by [ k ] = nil
29
34
@@ -44,31 +49,32 @@ def []=(k, v)
44
49
# Case-insensitive wrapper around hash lookup
45
50
#
46
51
def []( k )
47
- super ( find_key_case ( k ) )
52
+ super ( k . downcase )
48
53
end
49
54
50
55
#
51
56
# Case-insensitive wrapper around store
52
57
#
53
58
def store ( k , v )
54
- super ( find_key_case ( k ) , v )
59
+ add_key ( k )
60
+ super ( k . downcase , v )
55
61
end
56
62
57
63
#
58
64
# Case-insensitive wrapper around delete
59
65
#
60
66
def delete ( k )
61
- super ( find_key_case ( k ) )
67
+ super ( k . downcase )
62
68
end
63
69
64
-
65
- #
66
- # Updates a value in the datastore with the specified name, k, to the
67
- # specified value, v. This update does not alter the imported status of
68
- # the value.
69
- #
70
- def update_value ( k , v )
71
- self . store ( k , v )
70
+ # Override Hash's to_h method so we can include the original case of each key
71
+ # (failing to do this breaks a number of places in framework and pro that use
72
+ # serialized datastores)
73
+ def to_h
74
+ @original_keys . reduce ( { } ) do | acc , key |
75
+ acc [ key ] = self [ key ]
76
+ acc
77
+ end
72
78
end
73
79
74
80
#
@@ -128,15 +134,16 @@ def import_options_from_s(option_str, delim = nil)
128
134
# Imports options from a hash and stores them in the datastore.
129
135
#
130
136
def import_options_from_hash ( option_hash , imported = true , imported_by = nil )
131
- option_hash . each_pair { |key , val |
137
+ option_hash . each_pair do |key , val |
132
138
import_option ( key , val , imported , imported_by )
133
- }
139
+ end
134
140
end
135
141
136
142
def import_option ( key , val , imported = true , imported_by = nil , option = nil )
137
143
self . store ( key , val )
138
144
139
- @options [ key ] = option
145
+ key = key . downcase
146
+ @options [ key ] = option
140
147
@imported [ key ] = imported
141
148
@imported_by [ key ] = imported_by
142
149
end
@@ -145,21 +152,9 @@ def import_option(key, val, imported=true, imported_by=nil, option=nil)
145
152
# Serializes the options in the datastore to a string.
146
153
#
147
154
def to_s ( delim = ' ' )
148
- str = ''
149
-
150
- keys . sort . each { |key |
151
- str << "#{ key } =#{ self [ key ] } " + ( ( str . length ) ? delim : '' )
152
- }
153
-
154
- return str
155
- end
156
-
157
- def to_h
158
- datastore_hash = { }
159
- self . keys . each do |k |
160
- datastore_hash [ k . to_s ] = self [ k ] . to_s
155
+ @original_keys . reduce ( '' ) do |acc , key |
156
+ acc << "#{ key } =#{ self [ key ] } #{ delim } "
161
157
end
162
- datastore_hash
163
158
end
164
159
165
160
#
@@ -199,9 +194,10 @@ def from_file(path, name = 'global')
199
194
# not include default option values.
200
195
#
201
196
def user_defined
202
- reject { |k , v |
203
- @imported [ k ] == true
204
- }
197
+ @original_keys . reduce ( { } ) do |acc , k |
198
+ acc [ k ] = self [ k ] unless @imported [ k . downcase ]
199
+ acc
200
+ end
205
201
end
206
202
207
203
#
@@ -222,40 +218,26 @@ def clear_non_user_defined
222
218
# Completely clear all values in the hash
223
219
#
224
220
def clear
225
- self . keys . each { |k | self . delete ( k ) }
226
- self
221
+ @options . clear
222
+ @imported . clear
223
+ @imported_by . clear
224
+ @original_keys . clear
225
+ super
227
226
end
228
227
229
- #
230
- # Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
231
- # "can't add a new key into hash during iteration"
232
- #
228
+ # Yield the original-cased key
233
229
def each ( &block )
234
- list = [ ]
235
- self . keys . sort . each do |sidx |
236
- list << [ sidx , self [ sidx ] ]
230
+ @original_keys . each do |key |
231
+ block . call ( key , self [ key ] )
237
232
end
238
- list . each ( &block )
239
233
end
240
234
241
- protected
242
-
243
- #
244
- # Case-insensitive key lookup
245
- #
246
- def find_key_case ( k )
235
+ protected
247
236
248
- # Scan each key looking for a match
249
- self . each_key do |rk |
250
- if ( rk . downcase == k . downcase )
251
- return rk
252
- end
253
- end
254
-
255
- # Fall through to the non-existent value
256
- return k
237
+ # Keep track of the original, case-sensitive key
238
+ def add_key ( k )
239
+ @original_keys . add ( k ) unless include? k . downcase
257
240
end
258
-
259
241
end
260
242
261
243
###
@@ -278,7 +260,7 @@ def initialize(m)
278
260
# if we can't directly find it
279
261
#
280
262
def fetch ( key )
281
- key = find_key_case ( key )
263
+ key = key . downcase
282
264
val = nil
283
265
val = super if ( @imported_by [ key ] != 'self' )
284
266
if ( val . nil? and @_module and @_module . framework )
@@ -292,7 +274,7 @@ def fetch(key)
292
274
# Same as fetch
293
275
#
294
276
def []( key )
295
- key = find_key_case ( key )
277
+ key = key . downcase
296
278
val = nil
297
279
val = super if ( @imported_by [ key ] != 'self' )
298
280
if ( val . nil? and @_module and @_module . framework )
@@ -315,11 +297,10 @@ def default?(key)
315
297
def copy
316
298
clone = self . class . new ( @_module )
317
299
self . keys . each do |k |
318
- clone . import_option ( k , self [ k ] . kind_of? ( String ) ? self [ k ] . dup : self [ k ] , @imported [ k ] , @imported_by [ k ] )
300
+ clone . import_option ( k , self [ k ] . kind_of? ( String ) ? self [ k ] . dup : self [ k ] , @imported [ k . downcase ] , @imported_by [ k . downcase ] )
319
301
end
320
302
clone
321
303
end
322
304
end
323
305
324
306
end
325
-
0 commit comments