ronin 1.3.0 → 1.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/ChangeLog.md +51 -1
  2. data/Gemfile +1 -1
  3. data/README.md +36 -41
  4. data/bin/ronin-install +25 -0
  5. data/bin/ronin-uninstall +25 -0
  6. data/bin/ronin-update +25 -0
  7. data/gemspec.yml +4 -4
  8. data/lib/bond/completions/ronin.rb +6 -5
  9. data/lib/ronin/address.rb +0 -2
  10. data/lib/ronin/auto_load.rb +4 -5
  11. data/lib/ronin/campaign.rb +1 -0
  12. data/lib/ronin/credential.rb +6 -5
  13. data/lib/ronin/database/database.rb +1 -0
  14. data/lib/ronin/email_address.rb +28 -3
  15. data/lib/ronin/environment.rb +3 -6
  16. data/lib/ronin/host_name.rb +1 -3
  17. data/lib/ronin/host_name_ip_address.rb +0 -2
  18. data/lib/ronin/ip_address.rb +1 -4
  19. data/lib/ronin/ip_address_mac_address.rb +0 -2
  20. data/lib/ronin/mac_address.rb +0 -2
  21. data/lib/ronin/model/has_unique_name.rb +17 -0
  22. data/lib/ronin/open_port.rb +0 -4
  23. data/lib/ronin/organization.rb +0 -1
  24. data/lib/ronin/os.rb +0 -2
  25. data/lib/ronin/os_guess.rb +0 -2
  26. data/lib/ronin/password.rb +17 -1
  27. data/lib/ronin/port.rb +34 -1
  28. data/lib/ronin/repository.rb +31 -25
  29. data/lib/ronin/script/buildable.rb +19 -4
  30. data/lib/ronin/script/deployable.rb +7 -8
  31. data/lib/ronin/script/exceptions.rb +1 -0
  32. data/lib/ronin/script/exceptions/build_failed.rb +27 -0
  33. data/lib/ronin/script/path.rb +28 -11
  34. data/lib/ronin/script/script.rb +17 -14
  35. data/lib/ronin/script/testable.rb +3 -3
  36. data/lib/ronin/service.rb +0 -1
  37. data/lib/ronin/service_credential.rb +0 -1
  38. data/lib/ronin/software.rb +0 -1
  39. data/lib/ronin/target.rb +0 -2
  40. data/lib/ronin/tcp_port.rb +0 -1
  41. data/lib/ronin/ui/cli/class_command.rb +130 -0
  42. data/lib/ronin/ui/cli/command.rb +344 -159
  43. data/lib/ronin/ui/cli/commands/campaigns.rb +39 -29
  44. data/lib/ronin/ui/cli/commands/console.rb +24 -15
  45. data/lib/ronin/ui/cli/commands/creds.rb +14 -12
  46. data/lib/ronin/ui/cli/commands/database.rb +63 -46
  47. data/lib/ronin/ui/cli/commands/emails.rb +15 -15
  48. data/lib/ronin/ui/cli/commands/help.rb +6 -5
  49. data/lib/ronin/ui/cli/commands/hosts.rb +24 -24
  50. data/lib/ronin/ui/cli/commands/install.rb +104 -0
  51. data/lib/ronin/ui/cli/commands/ips.rb +23 -23
  52. data/lib/ronin/ui/cli/commands/repos.rb +69 -182
  53. data/lib/ronin/ui/cli/commands/uninstall.rb +65 -0
  54. data/lib/ronin/ui/cli/commands/update.rb +100 -0
  55. data/lib/ronin/ui/cli/commands/urls.rb +24 -24
  56. data/lib/ronin/ui/cli/model_command.rb +8 -6
  57. data/lib/ronin/ui/cli/printing.rb +167 -0
  58. data/lib/ronin/ui/cli/resources_command.rb +21 -13
  59. data/lib/ronin/ui/cli/script_command.rb +126 -24
  60. data/lib/ronin/ui/console/commands.rb +4 -1
  61. data/lib/ronin/ui/console/console.rb +1 -1
  62. data/lib/ronin/ui/console/context.rb +1 -1
  63. data/lib/ronin/url.rb +24 -5
  64. data/lib/ronin/url_query_param.rb +0 -1
  65. data/lib/ronin/version.rb +1 -1
  66. data/lib/ronin/web_credential.rb +0 -5
  67. data/spec/email_address_spec.rb +17 -0
  68. data/spec/installation_spec.rb +24 -34
  69. data/spec/model/has_authors_spec.rb +1 -1
  70. data/spec/model/has_description_spec.rb +1 -1
  71. data/spec/model/has_license_spec.rb +1 -1
  72. data/spec/model/has_name_spec.rb +1 -1
  73. data/spec/model/has_title_spec.rb +1 -1
  74. data/spec/model/has_version_spec.rb +1 -1
  75. data/spec/model/model_spec.rb +1 -1
  76. data/spec/repository_spec.rb +9 -9
  77. data/spec/script/script_spec.rb +2 -4
  78. data/spec/spec_helper.rb +16 -0
  79. data/spec/ui/cli/classes/test_command.rb +7 -3
  80. data/spec/ui/cli/command_spec.rb +37 -5
  81. metadata +82 -73
  82. data/spec/model/spec_helper.rb +0 -20
@@ -19,5 +19,6 @@
19
19
 
20
20
  require 'ronin/script/exceptions/exception'
21
21
  require 'ronin/script/exceptions/not_built'
22
+ require 'ronin/script/exceptions/build_failed'
22
23
  require 'ronin/script/exceptions/test_failed'
23
24
  require 'ronin/script/exceptions/deploy_failed'
@@ -0,0 +1,27 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin.
5
+ #
6
+ # Ronin is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/script/exceptions/exception'
21
+
22
+ module Ronin
23
+ module Script
24
+ class BuildFailed < Exception
25
+ end
26
+ end
27
+ end
@@ -18,7 +18,6 @@
18
18
  #
19
19
 
20
20
  require 'ronin/model'
21
- require 'ronin/repository'
22
21
  require 'ronin/script/script'
23
22
  require 'ronin/support/inflector'
24
23
 
@@ -231,10 +230,8 @@ module Ronin
231
230
  # destroy the cached file, if the actual file is missing
232
231
  return destroy
233
232
  elsif updated?
234
- if (script = cached_script)
235
- # destroy the previously cached object
236
- script.destroy!
237
- end
233
+ # clean the previously cached file
234
+ clean
238
235
 
239
236
  # if we couldn't cache anything, self-destruct
240
237
  destroy unless cache
@@ -244,6 +241,18 @@ module Ronin
244
241
  return false
245
242
  end
246
243
 
244
+ #
245
+ # Cleans the cached file from the Database.
246
+ #
247
+ # @since 1.4.0
248
+ #
249
+ def clean
250
+ if (script = cached_script)
251
+ # destroy the previously cached object
252
+ script.destroy!
253
+ end
254
+ end
255
+
247
256
  #
248
257
  # Before destroying the cached file object, also destroy the
249
258
  # associated cached object.
@@ -251,13 +260,21 @@ module Ronin
251
260
  # @since 1.1.0
252
261
  #
253
262
  def destroy
254
- unless destroyed?
255
- if (script = cached_script)
256
- script.destroy!
257
- end
258
- end
263
+ clean unless destroyed?
264
+
265
+ return super
266
+ end
267
+
268
+ #
269
+ # Before forcibly destroying the cached file object, also destroy the
270
+ # associated cached object.
271
+ #
272
+ # @since 1.4.0
273
+ #
274
+ def destroy!
275
+ clean unless destroyed?
259
276
 
260
- super
277
+ return super
261
278
  end
262
279
 
263
280
  #
@@ -83,6 +83,9 @@ module Ronin
83
83
 
84
84
  # The cached file of the object
85
85
  belongs_to :script_path, Ronin::Script::Path, :required => false
86
+
87
+ # Validations
88
+ validates_uniqueness_of :version, :scope => [:name]
86
89
  end
87
90
 
88
91
  Path.has 1, base.relationship_name, base, :child_key => [:script_path_id]
@@ -92,6 +95,20 @@ module Ronin
92
95
  # @since 1.1.0
93
96
  #
94
97
  module ClassMethods
98
+ #
99
+ # The shortened name of the Script class.
100
+ #
101
+ # @return [String]
102
+ # The shortened name.
103
+ #
104
+ # @since 1.4.0
105
+ #
106
+ # @api semipublic
107
+ #
108
+ def short_name
109
+ @short_name ||= self.name.split('::').last
110
+ end
111
+
95
112
  #
96
113
  # Loads the {Script} of the same class.
97
114
  #
@@ -177,20 +194,6 @@ module Ronin
177
194
  super(*arguments,&block)
178
195
  end
179
196
 
180
- #
181
- # The script type.
182
- #
183
- # @return [String]
184
- # The name of the script class.
185
- #
186
- # @since 1.1.0
187
- #
188
- # @api semipublic
189
- #
190
- def script_type
191
- @script_type ||= self.class.base_model.name.split('::').last
192
- end
193
-
194
197
  #
195
198
  # Determines if the original code, from the cache file, has been
196
199
  # loaded into the object.
@@ -50,7 +50,7 @@ module Ronin
50
50
  # Tests that the script is properly configured.
51
51
  #
52
52
  # @return [true]
53
- # The exploit is built and ready for deployment.
53
+ # The script is built and ready for deployment.
54
54
  #
55
55
  # @see test
56
56
  #
@@ -59,11 +59,11 @@ module Ronin
59
59
  # @api semipublic
60
60
  #
61
61
  def test!
62
- print_info "Testing #{script_type} ..."
62
+ print_info "Testing #{self.class.short_name} ..."
63
63
 
64
64
  @test_blocks.each { |block| block.call() }
65
65
 
66
- print_info "#{script_type} tested!"
66
+ print_info "#{self.class.short_name} tested!"
67
67
  return true
68
68
  end
69
69
 
data/lib/ronin/service.rb CHANGED
@@ -19,7 +19,6 @@
19
19
 
20
20
  require 'ronin/model'
21
21
  require 'ronin/model/has_unique_name'
22
- require 'ronin/open_port'
23
22
 
24
23
  module Ronin
25
24
  #
@@ -18,7 +18,6 @@
18
18
  #
19
19
 
20
20
  require 'ronin/credential'
21
- require 'ronin/open_port'
22
21
 
23
22
  module Ronin
24
23
  #
@@ -18,7 +18,6 @@
18
18
  #
19
19
 
20
20
  require 'ronin/model'
21
- require 'ronin/vendor'
22
21
 
23
22
  module Ronin
24
23
  #
data/lib/ronin/target.rb CHANGED
@@ -18,8 +18,6 @@
18
18
  #
19
19
 
20
20
  require 'ronin/model'
21
- require 'ronin/campaign'
22
- require 'ronin/address'
23
21
 
24
22
  require 'dm-timestamps'
25
23
  require 'fileutils'
@@ -18,7 +18,6 @@
18
18
  #
19
19
 
20
20
  require 'ronin/port'
21
- require 'ronin/url'
22
21
 
23
22
  module Ronin
24
23
  #
@@ -0,0 +1,130 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin.
5
+ #
6
+ # Ronin is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/ui/cli/command'
21
+
22
+ require 'parameters'
23
+
24
+ module Ronin
25
+ module UI
26
+ module CLI
27
+ #
28
+ # @since 1.4.0
29
+ #
30
+ class ClassCommand < Command
31
+
32
+ # The object created from the Class
33
+ attr_reader :object
34
+
35
+ def initialize(options={})
36
+ super(options)
37
+
38
+ unless self.class.command_class < Parameters
39
+ raise(TypeError,"#{command_class} does not include Parameters")
40
+ end
41
+
42
+ @object = self.class.command_class.new()
43
+ @object.params = options
44
+ end
45
+
46
+ protected
47
+
48
+ #
49
+ # Sets or gets the class namespace.
50
+ #
51
+ # @param [Module, nil] new_namespace
52
+ # The namespace to load the class from.
53
+ #
54
+ # @return [Module]
55
+ # The namespace that the class resides in.
56
+ #
57
+ # @api semipublic
58
+ #
59
+ def self.class_namespace(new_namespace=nil)
60
+ if new_namespace
61
+ @class_namespace = new_namespace
62
+ else
63
+ @class_namespace ||= if superclass < ClassCommand
64
+ superclass.class_namespace
65
+ end
66
+ end
67
+ end
68
+
69
+ #
70
+ # Sets or gets the class name.
71
+ #
72
+ # @param [Symbol, nil] name
73
+ # The class name to load from the namespace.
74
+ #
75
+ # @return [Symbol]
76
+ # The class name.
77
+ #
78
+ # @api semipublic
79
+ #
80
+ def self.class_name(name=nil)
81
+ if name
82
+ @class_name = name.to_sym
83
+ else
84
+ @class_name ||= self.name.split('::').last.to_sym
85
+ end
86
+ end
87
+
88
+ #
89
+ # The loaded class from the namespace.
90
+ #
91
+ # @return [Class]
92
+ # The loaded class.
93
+ #
94
+ # @api semipublic
95
+ #
96
+ def self.command_class
97
+ @command_class ||= class_namespace.const_get(class_name)
98
+ end
99
+
100
+ #
101
+ # Creates an OptionParser for the class command.
102
+ #
103
+ # @yield [opts]
104
+ # The given block will be passed the newly created OptionParser,
105
+ # after options for the class have been defined.
106
+ #
107
+ # @yieldparam [OptionParser] opts
108
+ # The newly created OptionParser.
109
+ #
110
+ # @return [OptionParser]
111
+ # The fully configured OptionParser.
112
+ #
113
+ # @since 1.4.0
114
+ #
115
+ # @api semipublic
116
+ #
117
+ def option_parser
118
+ super do |opts|
119
+ @object.each_param do |param|
120
+ Parameters::Options.define(opts,param)
121
+ end
122
+
123
+ yield opts if block_given?
124
+ end
125
+ end
126
+
127
+ end
128
+ end
129
+ end
130
+ end
@@ -17,19 +17,18 @@
17
17
  # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
18
  #
19
19
 
20
+ require 'ronin/ui/cli/printing'
20
21
  require 'ronin/ui/output'
21
- require 'ronin/ui/output/terminal'
22
22
  require 'ronin/support/inflector'
23
23
 
24
- require 'thor'
25
- require 'thor/group'
24
+ require 'parameters'
25
+ require 'parameters/options'
26
26
 
27
27
  module Ronin
28
28
  module UI
29
29
  module CLI
30
30
  #
31
- # The {Command} class inherits `Thor::Group` to provide a base-class
32
- # for defining commands for the {CLI}.
31
+ # The {Command} provides a base-class for defining commands for the {CLI}.
33
32
  #
34
33
  # # Extending
35
34
  #
@@ -45,29 +44,41 @@ module Ronin
45
44
  # module Commands
46
45
  # class MyCommand < Command
47
46
  #
48
- # desc 'My command'
47
+ # summary 'My command'
49
48
  #
50
- # # command options
51
- # class_option :stuff, :type => :boolean
52
- # class_option :syntax, :type => :string
53
- # class_option :includes, :type => :array
49
+ # option :enable, :type => true,
50
+ # :flag => '-e',
51
+ # :description => 'Enables stuff'
52
+ #
53
+ # option :syntax, :type => Symbol,
54
+ # :flag => '-S',
55
+ # :description => 'Syntax to use'
56
+ #
57
+ # option :includes, :type => Array[String],
58
+ # :default => [],
59
+ # :flag => '-I',
60
+ # :usage => 'FILE [...]',
61
+ # :description => 'Files to include'
62
+ #
63
+ # option :params, :type => Hash[Symbol => Object],
64
+ # :flag => '-P',
65
+ # :description => 'Additional params'
54
66
  #
55
- # # command arguments
56
67
  # argument :path
57
68
  #
58
69
  # #
59
70
  # # Executes the command.
60
71
  # #
61
72
  # def execute
62
- # print_info "Stuff enabled" if options.stuff?
73
+ # print_info "Stuff enabled" if enable?
63
74
  #
64
- # if options[:syntax]
65
- # print_info "Using syntax #{options[:syntax]}"
75
+ # if syntax?
76
+ # print_info "Using syntax #{@syntax}"
66
77
  # end
67
78
  #
68
- # if options[:includes]
79
+ # if includes?
69
80
  # print_info "Including:"
70
- # print_array options[:includes]
81
+ # print_array @includes
71
82
  # end
72
83
  # end
73
84
  #
@@ -79,14 +90,6 @@ module Ronin
79
90
  #
80
91
  # # Running
81
92
  #
82
- # To run the command from Ruby, one can call the {run} class method
83
- # with the options and arguments to run the command with:
84
- #
85
- # MyCommand.run(
86
- # {:stuff => true, :syntax => 'bla', :includes => ['other']},
87
- # ['some/file.txt']
88
- # )
89
- #
90
93
  # To run the command from Ruby, with raw command-line options, one
91
94
  # can call the `start` class method:
92
95
  #
@@ -110,33 +113,23 @@ module Ronin
110
113
  # ronin my_command some/file.txt --stuff --syntax bla \
111
114
  # --includes one two
112
115
  #
113
- class Command < Thor::Group
114
-
115
- include Thor::Actions
116
- include Output::Helpers
116
+ class Command
117
117
 
118
- class_option :verbose, :type => :boolean,
119
- :default => false,
120
- :aliases => '-v'
121
- class_option :quiet, :type => :boolean,
122
- :default => false,
123
- :aliases => '-q'
124
- class_option :silent, :type => :boolean,
125
- :default => false,
126
- :aliases => '-Q'
127
-
128
- class_option :color, :type => :boolean, :default => true
118
+ include Parameters
119
+ include Printing
129
120
 
130
121
  #
131
- # Sets the namespace of a new {Command} class.
122
+ # Initializes the command object.
132
123
  #
133
- # @param [Class] super_class
134
- # The new {Command} class.
124
+ # @param [Hash{Symbol => Object}] options
125
+ # Options for the command.
135
126
  #
136
- # @api private
127
+ # @api semipublic
137
128
  #
138
- def self.inherited(super_class)
139
- super_class.namespace(super_class.command_name)
129
+ def initialize(options={})
130
+ super()
131
+
132
+ initialize_params(options)
140
133
  end
141
134
 
142
135
  #
@@ -145,75 +138,122 @@ module Ronin
145
138
  # @api semipublic
146
139
  #
147
140
  def self.command_name
148
- Support::Inflector.underscore(self.name.split('::').last)
141
+ @command_name ||= Support::Inflector.underscore(
142
+ self.name.sub('Ronin::UI::CLI::Commands::','').gsub('::',':')
143
+ )
149
144
  end
150
145
 
151
146
  #
152
147
  # Runs the command.
153
148
  #
154
- # @param [Hash{String,Symbol => Object}] options
155
- # Option values for the command.
149
+ # @param [Array<String>] argv
150
+ # The arguments for the command to parse.
156
151
  #
157
- # @param [Array<String>] arguments
158
- # Additional arguments for the command.
152
+ # @return [true]
153
+ # Specifies that the command successfully executed.
159
154
  #
160
- # @return [Command]
161
- # The executed command.
155
+ # @note
156
+ # If the command raises an Exception, the process will exit with
157
+ # status code `-1`.
162
158
  #
163
159
  # @since 1.0.0
164
160
  #
165
161
  # @api public
166
162
  #
167
- def self.run(options={},arguments=[])
168
- command = self.new(arguments,options)
163
+ def self.start(argv=ARGV)
164
+ command = new()
165
+
166
+ unless command.start(argv)
167
+ exit -1
168
+ end
169
169
 
170
- command.invoke_all()
171
- return command
170
+ return true
172
171
  end
173
172
 
174
173
  #
175
- # Creates a new Command object.
174
+ # Runs the command with the given options.
176
175
  #
177
- # @param [Array] arguments
178
- # Command-line arguments.
176
+ # @param [Hash{Symbol => Object}] options
177
+ # Options for the command.
179
178
  #
180
- # @param [Array] opts
181
- # Additional command-line options.
179
+ # @return [true]
180
+ # Specifies that the command successfully executed.
182
181
  #
183
- # @param [Hash] config
184
- # Additional configuration.
182
+ # @raise [Exception]
183
+ # An exception raised within the command.
185
184
  #
186
- # @api semipublic
185
+ # @api public
187
186
  #
188
- def initialize(arguments=[],opts={},config={})
189
- @indent = 0
190
-
191
- super(arguments,opts,config)
192
- setup
187
+ def self.run(options={})
188
+ command = new()
189
+
190
+ return command.run(options)
193
191
  end
194
192
 
195
193
  #
196
- # Default method to call after the options have been parsed.
194
+ # Starts the command with the given command-line arguments.
195
+ #
196
+ # @param [Array<String>] argv
197
+ # The given command-line arguments.
198
+ #
199
+ # @return [true]
200
+ # Specifies whether the command executed successfully.
201
+ #
202
+ # @note
203
+ # If the command raises an Exception, the process will exit with
204
+ # status code `-1`.
205
+ #
206
+ # @since 1.4.0
197
207
  #
198
208
  # @api semipublic
199
209
  #
200
- def execute
201
- end
210
+ def start(argv=ARGV)
211
+ arguments = option_parser.parse(argv)
202
212
 
203
- protected
213
+ # set additional arguments
214
+ self.class.each_argument do |name|
215
+ param = get_param(name)
216
+
217
+ if param.type <= Parameters::Types::Array
218
+ # allow Array/Set arguments to collect all remaining args
219
+ param.value = arguments.shift(arguments.length)
220
+ else
221
+ param.value = arguments.shift
222
+ end
223
+ end
224
+
225
+ begin
226
+ run
227
+ rescue => error
228
+ print_exception(error)
229
+ exit -1
230
+ end
231
+
232
+ return true
233
+ end
204
234
 
205
235
  #
206
- # The banner for the command.
236
+ # Sets up and executes the command.
207
237
  #
208
- # @return [String]
209
- # The banner string.
238
+ # @param [Hash{Symbol => Object}] options
239
+ # Additional options to run the command with.
210
240
  #
211
- # @since 1.0.0
241
+ # @return [true]
242
+ # Specifies that the command exited successfully.
212
243
  #
213
- # @api private
244
+ # @see #setup
245
+ # @see #execute
214
246
  #
215
- def self.banner
216
- "ronin #{self_task.formatted_usage(self,false,true)}"
247
+ # @since 1.4.0
248
+ #
249
+ # @api semipublic
250
+ #
251
+ def run(options={})
252
+ self.params = options
253
+
254
+ setup
255
+ execute
256
+ return true
217
257
  end
218
258
 
219
259
  #
@@ -224,11 +264,11 @@ module Ronin
224
264
  # @api semipublic
225
265
  #
226
266
  def setup
227
- Output.verbose! if self.options.verbose?
228
- Output.quiet! if self.options.quiet?
229
- Output.silent! if self.options.silent?
267
+ Output.verbose! if verbose?
268
+ Output.quiet! if quiet?
269
+ Output.silent! if silent?
230
270
 
231
- Output.handler = if self.options.color?
271
+ Output.handler = if color?
232
272
  Output::Terminal::Color
233
273
  else
234
274
  Output::Terminal::Raw
@@ -236,145 +276,290 @@ module Ronin
236
276
  end
237
277
 
238
278
  #
239
- # Increases the indentation out output temporarily.
279
+ # Default method to call after the options have been parsed.
240
280
  #
241
- # @param [Integer] n
242
- # The number of spaces to increase the indentation by.
281
+ # @api semipublic
243
282
  #
244
- # @yield []
245
- # The block will be called after the indentation has been
246
- # increased. After the block has returned, the indentation will
247
- # be returned to normal.
283
+ def execute
284
+ end
285
+
286
+ protected
287
+
288
+ #
289
+ # The usage for the command.
290
+ #
291
+ # @param [String] new_usage
292
+ # The new usage for the command.
293
+ #
294
+ # @return [String]
295
+ # The usage string.
248
296
  #
249
- # @return [nil]
297
+ # @since 1.4.0
250
298
  #
251
299
  # @api semipublic
252
300
  #
253
- def indent(n=2)
254
- @indent += n
255
-
256
- yield
257
-
258
- @indent -= n
259
- return nil
301
+ def self.usage(new_usage=nil)
302
+ if new_usage
303
+ @usage = new_usage
304
+ else
305
+ @usage ||= if superclass < Command
306
+ superclass.usage
307
+ else
308
+ '[options]'
309
+ end
310
+ end
260
311
  end
261
312
 
262
313
  #
263
- # Print the given messages with indentation.
314
+ # The summary for the command.
264
315
  #
265
- # @param [Array] messages
266
- # The messages to print, one per-line.
316
+ # @param [String] new_summary
317
+ # The new summary for the command.
318
+ #
319
+ # @return [String]
320
+ # The summary string.
321
+ #
322
+ # @since 1.4.0
267
323
  #
268
324
  # @api semipublic
269
325
  #
270
- def puts(*messages)
271
- super(*(messages.map { |mesg| (' ' * @indent) + mesg.to_s }))
326
+ def self.summary(new_summary=nil)
327
+ if new_summary
328
+ @summary = new_summary
329
+ else
330
+ @summary ||= if superclass < Command
331
+ superclass.summary
332
+ end
333
+ end
272
334
  end
273
335
 
274
336
  #
275
- # Prints a given title.
337
+ # The options for the parameters.
276
338
  #
277
- # @param [String] title
278
- # The title to print.
339
+ # @return [Hash{Symbol => Hash}]
340
+ # The banner string.
341
+ #
342
+ # @since 1.4.0
279
343
  #
280
344
  # @api semipublic
281
345
  #
282
- def print_title(title)
283
- puts "[ #{title} ]\n"
346
+ def self.options
347
+ @options ||= {}
284
348
  end
285
349
 
286
350
  #
287
- # Prints a section with a title.
351
+ # Defines an option for the command.
288
352
  #
289
- # @yield []
290
- # The block will be called after the title has been printed
291
- # and indentation increased.
353
+ # @param [Symbol] name
354
+ # The name of the option.
292
355
  #
293
- # @since 1.0.0
356
+ # @param [Hash] options
357
+ # Additional options for the option.
358
+ #
359
+ # @option options [Hash{Class => Class}, Set<Class>, Array<Class>, Class, true] :type (String)
360
+ # The type of the options.
361
+ #
362
+ # @option options [Object, Proc] :default
363
+ # The default value for the options.
364
+ #
365
+ # @option options [String] :flag
366
+ # The short-flag for the option.
367
+ #
368
+ # @option options [String] :usage
369
+ # The usage for the option.
370
+ #
371
+ # @option options [String] :description
372
+ # The description of the option.
373
+ #
374
+ # @return [Parameters::ClassParam]
375
+ # The parameter that will contain the value of the option.
376
+ #
377
+ # @example
378
+ # option :output, :type => String,
379
+ # :flag => '-f',
380
+ # :usage => 'PATH',
381
+ # :description => 'The path to write the output to'
382
+ #
383
+ # @since 1.4.0
294
384
  #
295
385
  # @api semipublic
296
386
  #
297
- def print_section(title,&block)
298
- print_title(title)
299
- indent(&block)
387
+ def self.option(name,options={})
388
+ options = {:type => String}.merge(options)
389
+
390
+ self.options[name] = {
391
+ :flag => options[:flag],
392
+ :usage => options[:usage],
393
+ }
394
+
395
+ return parameter(name,options)
300
396
  end
301
397
 
398
+ option :verbose, :type => true,
399
+ :flag => '-v',
400
+ :description => 'Enable verbose output'
401
+
402
+ option :quiet, :type => true,
403
+ :flag => '-q',
404
+ :description => 'Disable verbose output'
405
+
406
+ option :silent, :type => true,
407
+ :description => 'Silence all output'
408
+
409
+ option :color, :type => true,
410
+ :default => proc { !$stdout.tty? },
411
+ :description => 'Enables color output'
412
+
302
413
  #
303
- # Prints a given Array.
414
+ # Enumerates through the options define by every sub-class.
304
415
  #
305
- # @param [Array] array
306
- # The Array to print.
416
+ # @yield [name,options]
417
+ # The given block will be passed each option.
307
418
  #
308
- # @param [Hash] options
309
- # Additional options.
419
+ # @yieldparam [Symbol] name
420
+ # The name of the option.
310
421
  #
311
- # @option options [String] :title
312
- # The optional title to print before the contents of the Array.
422
+ # @yieldparam [Hash] options
423
+ # Additional options associated with the option.
313
424
  #
314
- # @return [nil]
425
+ # @return [Enumerator]
426
+ # If no block is given, an Enumerator will be returned.
315
427
  #
316
- # @api semipublic
428
+ # @since 1.4.0
429
+ #
430
+ # @api private
317
431
  #
318
- def print_array(array,options={})
319
- print_title(options[:title]) if options[:title]
432
+ def self.each_option(&block)
433
+ return enum_for(:each_option) unless block
320
434
 
321
- indent do
322
- array.each { |value| puts value }
435
+ ancestors.reverse_each do |ancestor|
436
+ if ancestor <= Command
437
+ ancestor.options.each(&block)
438
+ end
323
439
  end
440
+ end
324
441
 
325
- puts if options[:title]
326
- return nil
442
+ #
443
+ # The additional arguments of the command.
444
+ #
445
+ # @return [Array<Symbol>]
446
+ # The names of the arguments.
447
+ #
448
+ # @since 1.4.0
449
+ #
450
+ # @api semipublic
451
+ #
452
+ def self.arguments
453
+ @arguments ||= []
327
454
  end
328
455
 
329
456
  #
330
- # Prints a given Hash.
457
+ # Defines an argument for the command.
331
458
  #
332
- # @param [Hash] hash
333
- # The Hash to print.
459
+ # @param [Symbol] name
460
+ # The name of the argument.
334
461
  #
335
462
  # @param [Hash] options
336
- # Additional options.
463
+ # Additional options for the argument.
464
+ #
465
+ # @option options [Hash{Class => Class}, Set<Class>, Array<Class>, Class, true] :type (String)
466
+ # The type of the argument.
467
+ #
468
+ # @option options [Object, Proc] :default
469
+ # The default value for the argument.
470
+ #
471
+ # @return [Parameters::ClassParam]
472
+ # The parameter that will contain the value of the argument.
337
473
  #
338
- # @option options [String] :title
339
- # The optional title to print before the contents of the Hash.
474
+ # @example
475
+ # argument :file, :type => String,
476
+ # :description => "The file to process"
340
477
  #
341
- # @return [nil]
478
+ # @since 1.4.0
342
479
  #
343
480
  # @api semipublic
344
481
  #
345
- def print_hash(hash,options={})
346
- align = hash.keys.map { |name|
347
- name.to_s.length
348
- }.max
482
+ def self.argument(name,options={})
483
+ self.arguments << name.to_sym
349
484
 
350
- print_title(options[:title]) if options[:title]
485
+ return parameter(name,options)
486
+ end
351
487
 
352
- indent do
353
- hash.each do |name,value|
354
- name = "#{name}:".ljust(align)
355
- puts "#{name}\t#{value}"
488
+ #
489
+ # Enumerates through the arguments define by every sub-class.
490
+ #
491
+ # @yield [name]
492
+ # The given block will be passed each argument name.
493
+ #
494
+ # @yieldparam [Symbol] name
495
+ # The name of the argument.
496
+ #
497
+ # @return [Enumerator]
498
+ # If no block is given, an Enumerator will be returned.
499
+ #
500
+ # @since 1.4.0
501
+ #
502
+ # @api private
503
+ #
504
+ def self.each_argument(&block)
505
+ return enum_for(:each_argument) unless block
506
+
507
+ ancestors.reverse_each do |ancestor|
508
+ if ancestor <= Command
509
+ ancestor.arguments.each(&block)
356
510
  end
357
511
  end
358
-
359
- puts if options[:title]
360
- return nil
361
512
  end
362
513
 
363
514
  #
364
- # Prints an exception and a shortened backtrace.
515
+ # Creates an OptionParser for the command.
365
516
  #
366
- # @param [Exception] exception
367
- # The exception to print.
517
+ # @yield [opts]
518
+ # The given block will be passed the new OptionParser, after
519
+ # all options have been defined, but before the Arguments have
520
+ # been listed.
368
521
  #
369
- # @since 1.0.0
522
+ # @yieldparam [OptionParser] opts
523
+ # The newly created OptionParser.
524
+ #
525
+ # @return [OptionParser]
526
+ # The new configured OptionParser.
527
+ #
528
+ # @since 1.4.0
370
529
  #
371
530
  # @api semipublic
372
531
  #
373
- def print_exception(exception)
374
- print_error exception.message
532
+ def option_parser
533
+ OptionParser.new do |opts|
534
+ opts.banner = "Usage: ronin #{self.class.command_name} #{self.class.usage}"
535
+
536
+ # append the arguments to the banner
537
+ self.class.each_argument do |name|
538
+ opts.banner << " #{name.to_s.upcase}"
539
+ end
540
+
541
+ opts.separator ''
542
+ opts.separator 'Options:'
543
+
544
+ self.class.each_option do |name,options|
545
+ Parameters::Options.define(opts,get_param(name),options)
546
+ end
547
+
548
+ yield opts if block_given?
549
+
550
+ opts.separator ''
551
+ opts.separator 'Arguments:'
552
+
553
+ self.class.each_argument do |name|
554
+ param = get_param(name)
375
555
 
376
- (0..5).each do |i|
377
- print_error ' ' + exception.backtrace[i]
556
+ opts.separator "\t#{name.to_s.upcase}\t#{param.description}"
557
+ end
558
+
559
+ if self.class.summary
560
+ opts.separator ''
561
+ opts.separator self.class.summary
562
+ end
378
563
  end
379
564
  end
380
565