ronin 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. data/COPYING.txt +339 -0
  2. data/History.txt +34 -0
  3. data/Manifest.txt +157 -0
  4. data/README.txt +131 -0
  5. data/Rakefile +23 -0
  6. data/TODO.txt +6 -0
  7. data/bin/ronin +12 -0
  8. data/lib/ronin.rb +35 -0
  9. data/lib/ronin/arch.rb +86 -0
  10. data/lib/ronin/author.rb +88 -0
  11. data/lib/ronin/cache.rb +27 -0
  12. data/lib/ronin/cache/config.rb +34 -0
  13. data/lib/ronin/cache/exceptions.rb +25 -0
  14. data/lib/ronin/cache/exceptions/extension_not_found.rb +29 -0
  15. data/lib/ronin/cache/exceptions/overlay_cached.rb +29 -0
  16. data/lib/ronin/cache/exceptions/overlay_not_found.rb +29 -0
  17. data/lib/ronin/cache/extension.rb +706 -0
  18. data/lib/ronin/cache/extension_cache.rb +108 -0
  19. data/lib/ronin/cache/overlay.rb +418 -0
  20. data/lib/ronin/cache/overlay_cache.rb +228 -0
  21. data/lib/ronin/cache/ronin.rb +50 -0
  22. data/lib/ronin/chars.rb +25 -0
  23. data/lib/ronin/chars/char_set.rb +121 -0
  24. data/lib/ronin/chars/chars.rb +180 -0
  25. data/lib/ronin/config.rb +31 -0
  26. data/lib/ronin/console.rb +127 -0
  27. data/lib/ronin/context.rb +233 -0
  28. data/lib/ronin/database.rb +122 -0
  29. data/lib/ronin/environment.rb +39 -0
  30. data/lib/ronin/exceptions/context_not_found.rb +27 -0
  31. data/lib/ronin/exceptions/invalid_database_config.rb +27 -0
  32. data/lib/ronin/exceptions/object_context_not_found.rb +27 -0
  33. data/lib/ronin/exceptions/unknown_context.rb +27 -0
  34. data/lib/ronin/exceptions/unknown_object_context.rb +27 -0
  35. data/lib/ronin/extensions.rb +28 -0
  36. data/lib/ronin/extensions/hash.rb +62 -0
  37. data/lib/ronin/extensions/kernel.rb +34 -0
  38. data/lib/ronin/extensions/meta.rb +24 -0
  39. data/lib/ronin/extensions/meta/object.rb +24 -0
  40. data/lib/ronin/extensions/string.rb +37 -0
  41. data/lib/ronin/extensions/uri.rb +24 -0
  42. data/lib/ronin/extensions/uri/http.rb +78 -0
  43. data/lib/ronin/extensions/uri/query_params.rb +97 -0
  44. data/lib/ronin/formatting.rb +29 -0
  45. data/lib/ronin/formatting/binary.rb +24 -0
  46. data/lib/ronin/formatting/digest.rb +24 -0
  47. data/lib/ronin/formatting/extensions.rb +26 -0
  48. data/lib/ronin/formatting/extensions/binary.rb +25 -0
  49. data/lib/ronin/formatting/extensions/binary/integer.rb +59 -0
  50. data/lib/ronin/formatting/extensions/binary/string.rb +73 -0
  51. data/lib/ronin/formatting/extensions/digest.rb +24 -0
  52. data/lib/ronin/formatting/extensions/digest/string.rb +65 -0
  53. data/lib/ronin/formatting/extensions/html.rb +24 -0
  54. data/lib/ronin/formatting/extensions/html/string.rb +75 -0
  55. data/lib/ronin/formatting/extensions/http.rb +24 -0
  56. data/lib/ronin/formatting/extensions/http/string.rb +69 -0
  57. data/lib/ronin/formatting/extensions/text.rb +24 -0
  58. data/lib/ronin/formatting/extensions/text/string.rb +96 -0
  59. data/lib/ronin/formatting/html.rb +24 -0
  60. data/lib/ronin/formatting/http.rb +24 -0
  61. data/lib/ronin/formatting/text.rb +24 -0
  62. data/lib/ronin/license.rb +87 -0
  63. data/lib/ronin/model.rb +44 -0
  64. data/lib/ronin/models.rb +34 -0
  65. data/lib/ronin/network.rb +31 -0
  66. data/lib/ronin/network/esmtp.rb +24 -0
  67. data/lib/ronin/network/extensions.rb +31 -0
  68. data/lib/ronin/network/extensions/esmtp.rb +24 -0
  69. data/lib/ronin/network/extensions/esmtp/net.rb +68 -0
  70. data/lib/ronin/network/extensions/http.rb +24 -0
  71. data/lib/ronin/network/extensions/http/net.rb +303 -0
  72. data/lib/ronin/network/extensions/imap.rb +24 -0
  73. data/lib/ronin/network/extensions/imap/net.rb +92 -0
  74. data/lib/ronin/network/extensions/pop3.rb +24 -0
  75. data/lib/ronin/network/extensions/pop3/net.rb +65 -0
  76. data/lib/ronin/network/extensions/smtp.rb +24 -0
  77. data/lib/ronin/network/extensions/smtp/net.rb +80 -0
  78. data/lib/ronin/network/extensions/tcp.rb +24 -0
  79. data/lib/ronin/network/extensions/tcp/net.rb +94 -0
  80. data/lib/ronin/network/extensions/telnet.rb +24 -0
  81. data/lib/ronin/network/extensions/telnet/net.rb +132 -0
  82. data/lib/ronin/network/extensions/udp.rb +24 -0
  83. data/lib/ronin/network/extensions/udp/net.rb +99 -0
  84. data/lib/ronin/network/http.rb +128 -0
  85. data/lib/ronin/network/http/exceptions.rb +24 -0
  86. data/lib/ronin/network/http/exceptions/unknown_request.rb +31 -0
  87. data/lib/ronin/network/imap.rb +47 -0
  88. data/lib/ronin/network/pop3.rb +47 -0
  89. data/lib/ronin/network/smtp.rb +26 -0
  90. data/lib/ronin/network/smtp/email.rb +126 -0
  91. data/lib/ronin/network/smtp/smtp.rb +55 -0
  92. data/lib/ronin/network/tcp.rb +24 -0
  93. data/lib/ronin/network/telnet.rb +95 -0
  94. data/lib/ronin/network/udp.rb +24 -0
  95. data/lib/ronin/object_context.rb +257 -0
  96. data/lib/ronin/objects.rb +29 -0
  97. data/lib/ronin/parameters.rb +27 -0
  98. data/lib/ronin/parameters/class_param.rb +45 -0
  99. data/lib/ronin/parameters/exceptions.rb +25 -0
  100. data/lib/ronin/parameters/exceptions/missing_param.rb +29 -0
  101. data/lib/ronin/parameters/exceptions/param_not_found.rb +29 -0
  102. data/lib/ronin/parameters/instance_param.rb +57 -0
  103. data/lib/ronin/parameters/param.rb +45 -0
  104. data/lib/ronin/parameters/parameters.rb +275 -0
  105. data/lib/ronin/path.rb +70 -0
  106. data/lib/ronin/pending_context.rb +42 -0
  107. data/lib/ronin/persistence.rb +32 -0
  108. data/lib/ronin/platform.rb +95 -0
  109. data/lib/ronin/product.rb +56 -0
  110. data/lib/ronin/ronin.rb +49 -0
  111. data/lib/ronin/rpc.rb +27 -0
  112. data/lib/ronin/rpc/call.rb +75 -0
  113. data/lib/ronin/rpc/client.rb +91 -0
  114. data/lib/ronin/rpc/console.rb +79 -0
  115. data/lib/ronin/rpc/exceptions.rb +25 -0
  116. data/lib/ronin/rpc/exceptions/not_implemented.rb +29 -0
  117. data/lib/ronin/rpc/exceptions/response_missing.rb +29 -0
  118. data/lib/ronin/rpc/interactive.rb +55 -0
  119. data/lib/ronin/rpc/interactive_console.rb +58 -0
  120. data/lib/ronin/rpc/interactive_shell.rb +59 -0
  121. data/lib/ronin/rpc/response.rb +57 -0
  122. data/lib/ronin/rpc/service.rb +69 -0
  123. data/lib/ronin/rpc/shell.rb +66 -0
  124. data/lib/ronin/runner.rb +24 -0
  125. data/lib/ronin/runner/program.rb +26 -0
  126. data/lib/ronin/runner/program/command.rb +204 -0
  127. data/lib/ronin/runner/program/commands.rb +33 -0
  128. data/lib/ronin/runner/program/commands/add.rb +73 -0
  129. data/lib/ronin/runner/program/commands/help.rb +52 -0
  130. data/lib/ronin/runner/program/commands/install.rb +65 -0
  131. data/lib/ronin/runner/program/commands/list.rb +81 -0
  132. data/lib/ronin/runner/program/commands/remove.rb +57 -0
  133. data/lib/ronin/runner/program/commands/uninstall.rb +57 -0
  134. data/lib/ronin/runner/program/commands/update.rb +55 -0
  135. data/lib/ronin/runner/program/exceptions.rb +24 -0
  136. data/lib/ronin/runner/program/exceptions/unknown_command.rb +31 -0
  137. data/lib/ronin/runner/program/options.rb +205 -0
  138. data/lib/ronin/runner/program/program.rb +173 -0
  139. data/lib/ronin/runner/program/runner.rb +35 -0
  140. data/lib/ronin/sessions.rb +32 -0
  141. data/lib/ronin/sessions/esmtp.rb +76 -0
  142. data/lib/ronin/sessions/imap.rb +73 -0
  143. data/lib/ronin/sessions/pop3.rb +70 -0
  144. data/lib/ronin/sessions/session.rb +52 -0
  145. data/lib/ronin/sessions/smtp.rb +76 -0
  146. data/lib/ronin/sessions/tcp.rb +111 -0
  147. data/lib/ronin/sessions/telnet.rb +76 -0
  148. data/lib/ronin/sessions/udp.rb +99 -0
  149. data/lib/ronin/sessions/web.rb +83 -0
  150. data/lib/ronin/shell.rb +81 -0
  151. data/lib/ronin/target.rb +40 -0
  152. data/lib/ronin/version.rb +27 -0
  153. data/lib/ronin/web.rb +24 -0
  154. data/lib/ronin/web/web.rb +265 -0
  155. data/spec/spec_helper.rb +9 -0
  156. data/tasks/spec.rb +7 -0
  157. metadata +324 -0
@@ -0,0 +1,25 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ require 'ronin/cache/exceptions/extension_not_found'
25
+ require 'ronin/cache/exceptions/overlay_not_found'
@@ -0,0 +1,29 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ module Ronin
25
+ module Cache
26
+ class ExtensionNotFound < RuntimeError
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ module Ronin
25
+ module Cache
26
+ class OverlayCached < RuntimeError
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ module Ronin
25
+ module Cache
26
+ class OverlayNotFound < RuntimeError
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,706 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ require 'ronin/cache/extension_cache'
25
+ require 'ronin/cache/overlay'
26
+ require 'ronin/context'
27
+
28
+ module Ronin
29
+ module Cache
30
+ class Extension
31
+
32
+ include Context
33
+
34
+ # Extension file name
35
+ EXTENSION_FILE = 'extension.rb'
36
+
37
+ # Extension lib directory
38
+ LIB_DIR = 'lib'
39
+
40
+ contextify :extension
41
+
42
+ # Name of extension
43
+ attr_reader :name
44
+
45
+ # Paths of similar extensions
46
+ attr_reader :paths
47
+
48
+ # Dependency extensions
49
+ attr_reader :dependencies
50
+
51
+ #
52
+ # Creates a new Extension with the specified _name_. If a
53
+ # _block_ is given, it will be passed the newly created
54
+ # Extension.
55
+ #
56
+ # Extension.new('exploits')
57
+ #
58
+ # Extension.new('awesome') do |ext|
59
+ # ...
60
+ # end
61
+ #
62
+ def initialize(name,&block)
63
+ @name = name.to_s
64
+ @paths = []
65
+ @dependencies = {}
66
+
67
+ @setup = false
68
+ @toredown = true
69
+
70
+ @setup_blocks = []
71
+ @action_blocks = {}
72
+ @teardown_blocks = []
73
+
74
+ block.call(self) if block
75
+ end
76
+
77
+ #
78
+ # Returns the names of all extensions within the overlay cache.
79
+ #
80
+ def Extension.names
81
+ Overlay.cache.overlays.map { |overlay| overlay.extensions }.flatten.uniq
82
+ end
83
+
84
+ #
85
+ # Returns +true+ if an extension exists with the specified _name_,
86
+ # returns +false+ otherwise.
87
+ #
88
+ def Extension.exists?(name)
89
+ Extension.names.include?(name.to_s)
90
+ end
91
+
92
+ #
93
+ # Iterates through the extension names passing each to the specified
94
+ # _block_.
95
+ #
96
+ # Extension.each_name do |name|
97
+ # puts name
98
+ # end
99
+ #
100
+ def Extension.each_name(&block)
101
+ Extension.names.each(&block)
102
+ end
103
+
104
+ #
105
+ # Returns the paths of all extensions.
106
+ #
107
+ def Extension.paths
108
+ paths = []
109
+
110
+ Overlay.each { |repo| paths += repo.extension_paths }
111
+
112
+ return paths
113
+ end
114
+
115
+ #
116
+ # Iterates over the paths of all extensions with the specified
117
+ # _name_, passing each to the specified _block_.
118
+ #
119
+ def Extension.each_path(&block)
120
+ Extension.paths.each(&block)
121
+ end
122
+
123
+ #
124
+ # Returns the paths of all extensions with the specified _name_.
125
+ #
126
+ def Extension.paths_for(name)
127
+ Overlay.with_extension(name).map do |repo|
128
+ File.expand_path(File.join(repo.path,name))
129
+ end
130
+ end
131
+
132
+ #
133
+ # Iterates over the paths of all extensions with the specified
134
+ # _name_, passing each to the specified _block_.
135
+ #
136
+ def Extension.each_path_for(name,&block)
137
+ Extension.paths_for(name).each(&block)
138
+ end
139
+
140
+ #
141
+ # Adds the lib/ directory from within the specified _path_ to
142
+ # $LOAD_PATH, only if the lib/ directory exists within the
143
+ # specified _path_ and the directory has not already been
144
+ # added to $LOAD_PATH. If a _block_ is given, it will be called
145
+ # after $LOAD_PATH may or maynot have been modified.
146
+ #
147
+ def Extension.load_path(path,&block)
148
+ lib_dir = File.expand_path(File.join(path,LIB_DIR))
149
+
150
+ if File.directory?(lib_dir)
151
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
152
+ end
153
+
154
+ block.call if block
155
+ return nil
156
+ end
157
+
158
+ #
159
+ # Similar to load_path, but adds the lib/ directories from the
160
+ # paths of all extensions with the specified _name_ to $LOAD_PATH.
161
+ # If a _block_ is given, it will be called after $LOAD_PATH may or
162
+ # maynot have been modified.
163
+ #
164
+ def Extension.load_paths(name,&block)
165
+ Extension.each_path_for(name) do |path|
166
+ Extension.load_path(path)
167
+ end
168
+
169
+ block.call if block
170
+ return nil
171
+ end
172
+
173
+ #
174
+ # Loads an extension at the specified _path_ into a newly created
175
+ # Extension object. If a _block_ is given, it will be passed the
176
+ # newly created Extension object.
177
+ #
178
+ def Extension.load_from(path,&block)
179
+ Extension.new(File.basename(name)) do |ext|
180
+ ext.include_path(path,&block)
181
+ end
182
+ end
183
+
184
+ #
185
+ # Loads an extension at the specified _path_ into a newly created
186
+ # Extension object and then runs it with the specified _block_.
187
+ #
188
+ # Extension.run_from('lab/exploits') do |ext|
189
+ # puts ext.search('apache')
190
+ # end
191
+ #
192
+ def Extension.run_from(path,&block)
193
+ Extension.load_from(path) do |ext|
194
+ ext.run(&block)
195
+ end
196
+ end
197
+
198
+ #
199
+ # Loads all extensions with the specified _name_ into a newly created
200
+ # Extension object. If a _block_ is given, it will be passed the
201
+ # newly created Extension object.
202
+ #
203
+ # Extension.load('shellcode') do |ext|
204
+ # puts ext.search('moon_lander')
205
+ # end
206
+ #
207
+ def Extension.load(name,&block)
208
+ Extension.new(name) do |ext|
209
+ ext.include(name,&block)
210
+ end
211
+ end
212
+
213
+ #
214
+ # Loads all extensions with the specified _name_ into a newly created
215
+ # Extension object and then runs it with the specified _block_.
216
+ #
217
+ # Extension.run('exploits') do |ext|
218
+ # puts ext.search(:product => 'Apache')
219
+ # end
220
+ #
221
+ def Extension.run(name,&block)
222
+ Extension.load(name) do |ext|
223
+ ext.run(&block)
224
+ end
225
+ end
226
+
227
+ #
228
+ # Returns the current ExtensionCache.
229
+ #
230
+ def Extension.cache
231
+ @@cache ||= ExtensionCache.new
232
+ end
233
+
234
+ #
235
+ # Returns the extension with the specified _name_ from the extension
236
+ # cache. If no extension exists with the specified _name_ an
237
+ # ExtensionNotFound exception will be raised.
238
+ #
239
+ def Extension.[](name)
240
+ Extension.cache[name]
241
+ end
242
+
243
+ #
244
+ # Returns +true+ if the extension with the specified _name_ has been
245
+ # loaded into the extension cache, returns +false+ otherwise.
246
+ #
247
+ def Extension.loaded?(name)
248
+ Extension.cache.has_extension?(name)
249
+ end
250
+
251
+ #
252
+ # Includes all extensions of the specified _name_ into the extension.
253
+ # If a _block_ is given, it will be passed the newly created
254
+ # extension after the extensions of _name_ have been included.
255
+ #
256
+ def include(name,&block)
257
+ Extension.load_paths(name) do
258
+ Extension.each_path_for(name) do |path|
259
+ include_path(path)
260
+ end
261
+ end
262
+
263
+ block.call(self) if block
264
+ return self
265
+ end
266
+
267
+ #
268
+ # Includes the extension at the specified _path_ into the extension.
269
+ # If a _block_ is given, it will be passed the newly created
270
+ # extension.
271
+ #
272
+ def include_path(path,&block)
273
+ path = File.expand_path(path)
274
+
275
+ unless File.directory?(path)
276
+ raise(ExtensionNotFound,"extension #{path.dump} is not a valid extension",caller)
277
+ end
278
+
279
+ # add to the search paths
280
+ @paths << path
281
+
282
+ Extension.load_path(path) do
283
+ extension_file = File.join(path,EXTENSION_FILE)
284
+
285
+ if File.file?(extension_file)
286
+ # instance_eval the extension block
287
+ context_block = Extension.load_context_block(extension_file)
288
+
289
+ instance_eval(&context_block) if context_block
290
+ end
291
+ end
292
+
293
+ block.call(self) if block
294
+ return self
295
+ end
296
+
297
+ #
298
+ # Loads all similar extensions with the specified _name_ into a
299
+ # newly created Extension object and adds it to the extensions
300
+ # dependencies.
301
+ #
302
+ # depend 'shellcode'
303
+ #
304
+ def depend(name)
305
+ name = name.to_s
306
+
307
+ unless Extension.exists?(name)
308
+ raise(ExtensionNotFound,"extension #{name.dump} is not in the overlay cache",caller)
309
+ end
310
+
311
+ @dependencies[name] ||= Extension.load(name)
312
+ return self
313
+ end
314
+
315
+ #
316
+ # Returns +true+ if the extension has the dependency of the specified
317
+ # _name_, returns +false+ otherwise.
318
+ #
319
+ def depends_on?(name)
320
+ @dependencies.has_key?(name.to_s)
321
+ end
322
+
323
+ #
324
+ # Passes all the extension's dependencies and the extension itself to
325
+ # the specified _block_ using the given _options_.
326
+ #
327
+ # _options_ may include the following keys:
328
+ # <tt>:top_down</tt>:: Indicates that distribute will recurse through
329
+ # the extensions and their elements in a
330
+ # top-down manner. This is distributes default
331
+ # behavior.
332
+ # <tt>:bottom_up</tt>:: Indictates that distribute will recurse
333
+ # through the extensions and their dependencies
334
+ # in a bottom-up manner. Mutually exclusive with
335
+ # the <tt>:top_down</tt> option.
336
+ #
337
+ def distribute(options={},&block)
338
+ distribute_deps = lambda {
339
+ @dependencies.map { |ext|
340
+ ext.distribute(options,&block)
341
+ }.flatten
342
+ }
343
+
344
+ if options[:bottom_up]
345
+ return distribute_deps.call + [block.call(self)]
346
+ else
347
+ return [block.call(self)] + distribute_deps.call
348
+ end
349
+ end
350
+
351
+ #
352
+ # Returns +true+ if the app context has a public instance method
353
+ # of the matching _name_, returns +false+ otherwise.
354
+ #
355
+ # ext.has_method?(:console) # => true
356
+ #
357
+ def has_method?(name)
358
+ public_methods.include?(name.to_s)
359
+ end
360
+
361
+ #
362
+ # Returns an +Array+ of extensions that have the specified _method_.
363
+ # If a _block_ is given, it will be passed each extension with the
364
+ # specified _method_.
365
+ #
366
+ # ext.extensions_with_method(:console) # => [...]
367
+ #
368
+ # ext.extensions_with_method(:console) do |ext|
369
+ # ext.console(ARGV)
370
+ # end
371
+ #
372
+ def extensions_with_method(method,&block)
373
+ extensions = distribute { |ext|
374
+ ext if ext.has_method?(method)
375
+ }.compact
376
+
377
+ extensions.each(&block) if block
378
+ return extensions
379
+ end
380
+
381
+ #
382
+ # Calls the setup blocks of the extension's dependencies and the
383
+ # extension itself. If a _block_ is given, it will be passed the
384
+ # extension after it has been setup.
385
+ #
386
+ # ext.perform_setup # => Extension
387
+ #
388
+ # ext.perform_setup do |ext|
389
+ # puts "Extension #{ext} has been setup..."
390
+ # end
391
+ #
392
+ def perform_setup(&block)
393
+ unless @setup
394
+ distribute(:bottom_up => true) do |ext|
395
+ ext.instance_eval do
396
+ @setup_blocks.each do |setup_block|
397
+ setup_block.call(self) if setup_block
398
+ end
399
+ end
400
+ end
401
+
402
+ @setup = true
403
+ @toredown = false
404
+ end
405
+
406
+ block.call(self) if block
407
+ return self
408
+ end
409
+
410
+ #
411
+ # Returns +true+ if the extension has been setup, returns +false+
412
+ # otherwise.
413
+ #
414
+ def was_setup?
415
+ @setup == true
416
+ end
417
+
418
+ #
419
+ # Run the teardown blocks of the extension and it's dependencies.
420
+ # If a _block_ is given, it will be passed the extension before it
421
+ # has been tore down.
422
+ #
423
+ # ext.perform_teardown # => Extension
424
+ #
425
+ # ext.perform_teardown do |ext|
426
+ # puts "Extension #{ext} is being tore down..."
427
+ # end
428
+ #
429
+ def perform_teardown(&block)
430
+ block.call(self) if block
431
+
432
+ unless @toredown
433
+ distribute(:top_down => true) do |ext|
434
+ ext.instance_eval do
435
+ @teardown_blocks.each do |teardown_block|
436
+ teardown_block.call(self) if teardown_block
437
+ end
438
+ end
439
+ end
440
+
441
+ @toredown = true
442
+ @setup = false
443
+ end
444
+
445
+ return self
446
+ end
447
+
448
+ #
449
+ # Returns +true+ if the extension has been toredown, returns +false+
450
+ # otherwise.
451
+ #
452
+ def was_toredown?
453
+ @toredown == true
454
+ end
455
+
456
+ #
457
+ # Sets up the extension, passes the extension to the specified
458
+ # _block_ and then tears down the extension.
459
+ #
460
+ # ext.run do |ext|
461
+ # ext.console(ARGV)
462
+ # end
463
+ #
464
+ def run(&block)
465
+ perform_setup
466
+
467
+ block.call(self) if block
468
+
469
+ perform_teardown
470
+ return self
471
+ end
472
+
473
+ #
474
+ # Returns an +Array+ of the names of all actions defined in the
475
+ # extension.
476
+ #
477
+ # ext.actions # => [...]
478
+ #
479
+ def actions
480
+ @action_blocks.keys
481
+ end
482
+
483
+ #
484
+ # Returns +true+ if the extension has the action of the specified
485
+ # _name_, returns +false+ otherwise.
486
+ #
487
+ def has_action?(name)
488
+ @action_blocks.has_key?(name.to_sym)
489
+ end
490
+
491
+ #
492
+ # Runs the action of the specified _name_ with the given _args_.
493
+ # If no action of the specified name exists, then an UnknownAction
494
+ # exception will be raised.
495
+ #
496
+ def perform_action(name,*args)
497
+ name = name.to_s
498
+
499
+ unless has_action?(name)
500
+ raise(UnknownAction,"action #{name.dump} is not defined",caller)
501
+ end
502
+
503
+ return run do
504
+ @action_blocks[name.to_sym].call(*args)
505
+ end
506
+ end
507
+
508
+ #
509
+ # Find the specified _path_ from within all similar extensions.
510
+ # If a _block_ is given, it will be passed the full path if found.
511
+ #
512
+ # ext.find_path('data/test')
513
+ #
514
+ # ext.find_path('data/test') do |path|
515
+ # puts Dir[File.join(path,'*')]
516
+ # end
517
+ #
518
+ def find_path(path,&block)
519
+ @paths.each do |ext_path|
520
+ full_path = File.expand_path(File.join(ext_path,path))
521
+
522
+ if File.exists?(full_path)
523
+ block.call(full_path) if block
524
+ return full_path
525
+ end
526
+ end
527
+
528
+ return nil
529
+ end
530
+
531
+ #
532
+ # Find the specified file _path_ from within all similar extensions.
533
+ # If a _block_ is given, it will be passed the full file path if
534
+ # found.
535
+ #
536
+ # ext.find_file('data/test/file.xml')
537
+ #
538
+ # ext.find_file('data/test/file.xml') do |file|
539
+ # REXML::Document.new(open(file))
540
+ # ...
541
+ # end
542
+ #
543
+ def find_file(path,&block)
544
+ find_path(path) do |full_path|
545
+ if File.file?(full_path)
546
+ block.call(full_path) if block
547
+ return full_path
548
+ end
549
+ end
550
+ end
551
+
552
+ #
553
+ # Find the specified directory _path_ from within all similar
554
+ # extensions. If a _block_ is given, it will be passed the full
555
+ # directory path if found.
556
+ #
557
+ # ext.find_directory('data/test')
558
+ #
559
+ # ext.find_directory('data/test') do |dir|
560
+ # puts Dir[File.join(dir,'*')]
561
+ # end
562
+ #
563
+ def find_dir(path,&block)
564
+ find_path(path) do |full_path|
565
+ if File.directory?(full_path)
566
+ block.call(full_path) if block
567
+ return full_path
568
+ end
569
+ end
570
+ end
571
+
572
+ #
573
+ # Find the paths that match the given pattern from within all similar
574
+ # extensions. If a _block_ is given, it will be passed each matching
575
+ # full path.
576
+ #
577
+ # ext.glob_paths('data/*') # => [...]
578
+ #
579
+ # ext.glob_paths('data/*') do |path|
580
+ # puts path
581
+ # end
582
+ #
583
+ def glob_paths(pattern,&block)
584
+ full_paths = @paths.inject([]) do |paths,ext_path|
585
+ paths + Dir[File.join(ext_path,pattern)]
586
+ end
587
+
588
+ full_paths.each(&block) if block
589
+ return full_paths
590
+ end
591
+
592
+ #
593
+ # Find the file paths that match the given pattern from within all
594
+ # similar extensions. If a _block_ is given, it will be passed each
595
+ # matching full file path.
596
+ #
597
+ # ext.glob_files('data/*.xml') # => [...]
598
+ #
599
+ # ext.glob_files('data/*.xml') do |file|
600
+ # puts file
601
+ # end
602
+ #
603
+ def glob_files(pattern,&block)
604
+ full_paths = glob_paths(pattern).select do |path|
605
+ File.file?(path)
606
+ end
607
+
608
+ full_paths.each(&block) if block
609
+ return full_paths
610
+ end
611
+
612
+ #
613
+ # Find the directory paths that match the given pattern from within
614
+ # all similar extensions. If a _block_ is given, it will be passed
615
+ # each matching full directory path.
616
+ #
617
+ # ext.glob_dirs('builds/*') # => [...]
618
+ #
619
+ # ext.glob_dirs('builds/*') do |dir|
620
+ # puts dir
621
+ # end
622
+ #
623
+ def glob_dirs(pattern,&block)
624
+ full_paths = glob_paths(pattern).select do |path|
625
+ File.directory?(path)
626
+ end
627
+
628
+ full_paths.each(&block) if block
629
+ return full_paths
630
+ end
631
+
632
+ #
633
+ # Returns the name of the app context in string form.
634
+ #
635
+ def to_s
636
+ @name.to_s
637
+ end
638
+
639
+ protected
640
+
641
+ #
642
+ # Adds the specified _block_ to the list of blocks to run in order
643
+ # to properly setup the extension.
644
+ #
645
+ def setup(&block)
646
+ @setup_blocks << block if block
647
+ return self
648
+ end
649
+
650
+ #
651
+ # Defines a new action with the specified _name_ and the given
652
+ # _block_. If an action of the same _name_ has already been defined
653
+ # then an ActionRedefined exception will be raised.
654
+ #
655
+ def action(name,&block)
656
+ name = name.to_s
657
+
658
+ if has_action?(name)
659
+ raise(ActionRedefined,"action #{name.dump} previously defined",caller)
660
+ end
661
+
662
+ @action_blocks[name.to_sym] = block
663
+ return self
664
+ end
665
+
666
+ #
667
+ # Adds the specified _block_ to the list of blocks to run in order
668
+ # to properly tear-down the extension.
669
+ #
670
+ def teardown(&block)
671
+ @teardown_blocks << block if block
672
+ return self
673
+ end
674
+
675
+ #
676
+ # Provides transparent access to the performing of actions
677
+ # and extensions dependencies.
678
+ #
679
+ # ext.scan('localhost') # => Extension
680
+ #
681
+ # ext.shellcode # => Extension
682
+ #
683
+ # ext.shellcode do |dep|
684
+ # puts "#{ext} has the dependency #{dep}"
685
+ # end
686
+ #
687
+ def method_missing(sym,*args,&block)
688
+ if (args.length==0)
689
+ name = sym.to_s
690
+
691
+ if (has_action?(name) && block.nil?)
692
+ return perform_action(name,*args)
693
+ end
694
+
695
+ if depends_on?(name)
696
+ block.call(@dependencies[name]) if block
697
+ return @dependencies[name]
698
+ end
699
+ end
700
+
701
+ return super(sym,*args,&block)
702
+ end
703
+
704
+ end
705
+ end
706
+ end