forked from iagox86/dnscat2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
settings.rb
190 lines (156 loc) · 5.06 KB
/
settings.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
##
# settings.rb
# By Ron Bowes
# February 8, 2014
#
# See LICENSE.md
#
# This is a class for managing ephemeral settings for a project.
#
# When the program starts, any number of settings can be registered either for
# individually created instances of this class, or for a global instances -
# Settings::GLOBAL - that is automatically created (and that is used for
# getting/setting settings that don't exist).
#
# What makes this more useful than a hash is two things:
#
# 1. Mutators - Each setting can have a mutator (which is built-in and related
# to the type) that can alter it. For example, TYPE_BOOLEAN has a mutator that
# changes 't', 'true', 'y', 'yes', etc to true. TYPE_INTEGER converts to a
# proper number (or throws an error if it's not a number), and so on.
#
# 2. Validators/callbacks - When a setting is defined, it's given a block that
# executes whenever the value changes. That block can either prevent the change
# by raising a Settings::ValidationError (which the program has to catch), or
# can process the change in some way (by, for example, changing a global
# variable).
#
# Together, those features make this class fairly flexible and useful!
##
class Settings
def initialize()
@settings = {}
end
GLOBAL = Settings.new()
class ValidationError < StandardError
end
TYPE_STRING = 0
TYPE_INTEGER = 1
TYPE_BOOLEAN = 2
TYPE_BLANK_IS_NIL = 3
TYPE_NO_STRIP = 4
@@mutators = {
TYPE_STRING => Proc.new() do |value|
value.strip()
end,
TYPE_INTEGER => Proc.new() do |value|
if(value.nil?)
raise(Settings::ValidationError, "Can't be nil!")
end
if(value.start_with?('0x'))
if(value[2..-1] !~ /^[\h]+$/)
raise(Settings::ValidationError, "Not a value hex string: #{value}")
end
value[2..-1].to_i(16)
else
if(value !~ /^[\d]+$/)
raise(Settings::ValidationError, "Not a valid number: #{value}")
end
value.to_i()
end
end,
TYPE_BOOLEAN => Proc.new() do |value|
value = value.downcase()
if(['t', 1, 'y', 'true', 'yes'].index(value))
# return
true
elsif(['f', 0, 'n', 'false', 'no'].index(value))
# return
false
else
raise(Settings::ValidationError, "Expected: true/false")
end
end,
TYPE_BLANK_IS_NIL => Proc.new() do |value|
value == '' ? nil : value.strip()
end,
TYPE_NO_STRIP => Proc.new() do |value|
value
end,
}
# Set the name to the new value. The name has to have previously been defined
# by calling the create() function.
#
# If this isn't Settings::GLOBAL and allow_recursion is set, unrecognized
# variables will be retrieved, if possible, from Settings::GLOBAL.
def set(name, new_value, allow_recursion=true)
name = name.to_s()
new_value = new_value.to_s()
if(@settings[name].nil?)
if(!allow_recursion)
raise(Settings::ValidationError, "No such setting!")
end
return Settings::GLOBAL.set(name, new_value, false)
end
old_value = @settings[name][:value]
new_value = @@mutators[@settings[name][:type]].call(new_value)
if(@settings[name][:watcher] && old_value != new_value)
@settings[name][:watcher].call(old_value, new_value)
end
@settings[name][:value] = new_value
return old_value
end
# Set a variable back to the default value.
def unset(name, allow_recursion=true)
if(@settings[name].nil?)
if(!allow_recursion)
raise(Settings::ValidationError, "No such setting!")
end
return Settings::GLOBAL.unset(name)
end
set(name, @settings[name][:default].to_s(), allow_recursion)
end
# Get the current value of a variable.
def get(name, allow_recursion=true)
name = name.to_s()
if(@settings[name].nil?)
if(allow_recursion)
return GLOBAL.get(name, false)
end
end
return @settings[name][:value]
end
# Yields for each setting. Each setting has a name, a value, a documentation
# string, and a default value.
def each_setting()
@settings.each_pair do |k, v|
yield(k, v[:value], v[:docs], v[:default])
end
end
# Create a new setting, or replace an old one. This must be done before a
# setting is used.
def create(name, type, default_value, docs)
name = name.to_s()
@settings[name] = @settings[name] || {}
@settings[name][:type] = type
@settings[name][:watcher] = proc
@settings[name][:docs] = docs
@settings[name][:default] = @@mutators[type].call(default_value.to_s())
# This sets it to the default value
unset(name, false)
end
# Replaces any variable found in the given, in the form '$var' where 'var'
# is a setting, with the setting value
#
# For example if "id" is set to 123, then the string "the id is $id" will
# become "the id is 123".
def do_replace(str, allow_recursion = true)
@settings.each_pair do |name, setting|
str = str.gsub(/\$#{name}/, setting[:value].to_s())
end
if(allow_recursion)
str = Settings::GLOBAL.do_replace(str, false)
end
return str
end
end