# # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of Ronin. # # Ronin is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ronin is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ronin. If not, see . # require 'ronin/script/path' require 'ronin/model/model' require 'ronin/model/has_name' require 'ronin/model/has_description' require 'ronin/model/has_version' require 'ronin/model/has_license' require 'ronin/model/has_authors' require 'ronin/ui/output/helpers' require 'object_loader' require 'data_paths/finders' require 'parameters' module Ronin # # {Script} is a high-level module that enables a Class to load # instances from files and cache them into the {Database}. # Classes that include {Script}, may also include Script sub-modules # that define behaviors of the Script Class: # # * {Buildable} # * {Testable} # * {Deployable} # # @since 1.1.0 # module Script include UI::Output::Helpers # # Adds the following to the Class. # # * {Script::InstanceMethods} # * {Model} # * {Model::HasName} # * {Model::HasDescription} # * {Model::HasVersion} # * {Model::HasLicense} # * {Model::HasAuthors} # * [ObjectLoader](http://rubydoc.info/gems/object_loader) # * [DataPaths::Finders](http://rubydoc.info/gems/data_paths) # * [Parameters](http://rubydoc.info/gems/parameters) # * {ClassMethods} # # @api semipublic # def self.included(base) base.send :include, InstanceMethods, Model, Model::HasName, Model::HasDescription, Model::HasVersion, Model::HasLicense, Model::HasAuthors, ObjectLoader, DataPaths::Finders, Parameters base.send :extend, ClassMethods base.module_eval do # The class-name of the cached object property :type, DataMapper::Property::Discriminator # The cached file of the object belongs_to :script_path, Ronin::Script::Path, :required => false # Validations validates_uniqueness_of :version, :scope => [:name] end Path.has 1, base.relationship_name, base, :child_key => [:script_path_id] end # # @since 1.1.0 # module ClassMethods # # The shortened name of the Script class. # # @return [String] # The shortened name. # # @since 1.4.0 # # @api semipublic # def short_name @short_name ||= self.name.split('::').last end # # Loads the {Script} of the same class. # # @param [String] path # The path to load the script from. # # @return [Script] # The loaded script. # # @example # Exploits::HTTP.load_from('mod_php_exploit.rb') # # => # # # @since 1.1.0 # def load_from(path) load_object(path) end # # Loads all objects with the matching attributes. # # @param [Hash] attributes # Attributes to search for. # # @since 1.1.0 # # @api public # def load_all(attributes={}) resources = all(attributes) resources.each { |resource| resource.load_script! } return resources end # # Loads the first object with matching attributes. # # @param [Hash] attributes # Attributes to search for. # # @return [Cacheable] # The loaded cached objects. # # @since 1.1.0 # # @api public # def load_first(attributes={}) if (resource = first(attributes)) resource.load_script! end return resource end end # # Instance methods for an {Script}. # # @since 1.1.0 # module InstanceMethods # # Initializes the Ronin Script. # # @param [Array] arguments # Arguments for initializing the Script. # # @since 1.1.0 # # @api semipublic # def initialize(*arguments,&block) @script_loaded = false @cache_prepared = false if arguments.first.kind_of?(Hash) initialize_params(arguments.first) end super(*arguments,&block) end # # Determines if the original code, from the cache file, has been # loaded into the object. # # @return [Boolean] # Specifies whether the original code has been loaded into the # object. # # @since 1.1.0 # # @api private # def script_loaded? @script_loaded == true end # # Loads the code from the cached file for the object, and instance # evaluates it into the object. # # @return [Boolean] # Indicates the original code was successfully loaded. # # @since 1.1.0 # # @api private # def load_script! if (cached? && !script_loaded?) block = self.class.load_object_block(self.script_path.path) @script_loaded = true instance_eval(&block) if block return true end return false end # # @return [Boolean] # Specifies whether the object has been prepared to be cached, # # @since 1.1.0 # # @api private # def prepared_for_cache? @cache_prepared == true end # # Indicates whether the object has been cached. # # @return [Boolean] # Specifies whether the object has been previously cached. # # @since 1.1.0 # # @api private # def cached? (saved? && self.script_path) end # # Default method which invokes the script. # # @param [Array] arguments # Optional arguments. # # @since 1.1.0 # def run(*arguments) end # # Converts the script to a String. # # @return [String] # The name and version of the script. # # @since 1.1.0 # # @api public # def to_s if (self.name && self.version) "#{self.name} #{self.version}" elsif self.name super elsif self.version self.version.to_s end end # # Inspects both the properties and parameters of the Ronin Script. # # @return [String] # The inspected Ronin Script. # # @since 1.1.0 # # @api public # def inspect body = [] self.attributes.each do |name,value| body << "#{name}: #{value.inspect}" end param_pairs = [] self.params.each do |name,param| param_pairs << "#{name}: #{param.value.inspect}" end body << "params: {#{param_pairs.join(', ')}}" return "#<#{self.class}: #{body.join(', ')}>" end protected # # Will call the given block only once, in order to prepare the # object for caching. # # @yield [] # The block will be ran inside the object when the object is to be # prepared for caching. # # @return [Boolean] # Specifies whether the object was successfully prepared for # caching. # # @since 1.1.0 # # @api public # def cache if (block_given? && !(cached? || prepared_for_cache?)) @cache_prepared = true yield return true end return false end end # # Loads a script from a file. # # @param [String] path # The path to the file. # # @return [Script] # The loaded script. # # @since 1.1.0 # # @api semipublic # def Script.load_from(path) path = File.expand_path(path) script = ObjectLoader.load_objects(path).find do |obj| obj.class < Script end unless script raise("No cacheable object defined in #{path.dump}") end script.instance_variable_set('@script_loaded',true) script.script_path = Path.new( :path => path, :timestamp => File.mtime(path), :class_name => script.class.to_s ) return script end end end