#
#--
# Ronin - A Ruby platform designed for information security and data
# exploration tasks.
#
# Copyright (c) 2006-2008 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#++
#
require 'ronin/cache/extension'
require 'ronin/cache/exceptions/extension_not_found'
require 'ronin/cache/overlay_cache'
require 'ronin/cache/config'
require 'ronin/persistence'
require 'rexml/document'
require 'repertoire'
module Ronin
module Cache
class Overlay < Repertoire::Repository
# Overlay metadata XML file name
METADATA_FILE = 'ronin.xml'
# Overlay objects directory
OBJECTS_DIR = 'objects'
# Local path to the overlay
attr_reader :path
# Source URI of the overlay source
attr_reader :uri
# Name of the overlay
attr_reader :name
# Authors of the overlay
attr_reader :authors
# License that the overlay contents is under
attr_reader :license
# Description
attr_reader :description
#
# Creates a new Overlay object with the specified _path_, _media_type_
# and _uri_.
#
def initialize(path,media_type=:nil,uri=nil,&block)
@path = File.expand_path(path)
@uri = uri
super(@path,Repertoire::Media.types[media_type])
load_metadata(&block)
end
#
# Load the Overlay Cache from the given _path_. If _path is not
# given, it will default to Config::REPOSITORY_CACHE_PATH.
# If a _block_ is given it will be passed the loaded Overlay Cache.
#
# Overlay.load_cache # => Cache
#
# Overlay.load_cache('/custom/cache') # => Cache
#
def Overlay.load_cache(path=Config::OVERLAY_CACHE_PATH,&block)
@@cache = OverlayCache.new(path,&block)
end
#
# Returns the current OverlayCache, or loads the default Cache
# if not already loaded.
#
def Overlay.cache
@@cache ||= load_cache
end
#
# Saves the overlay cache. If a _block_ is given, it will be passed
# the overlay cache before being saved.
#
# Overlay.save_cache # => OverlayCache
#
# Overlay.save_cahce do |cache|
# puts "Saving cache #{cache}"
# end
#
def Overlay.save_cache(&block)
Overlay.cache.save(&block)
end
#
# Returns the overlay with the specified _name_ from the overlay
# cache. If no such overlay exists, +nil+ is returned.
#
# Overlay['awesome']
#
def Overlay.[](name)
Overlay.cache[name]
end
#
# Returns +true+ if there is a overlay with the specified _name_
# in the overlay cache, returns +false+ otherwise.
#
def Overlay.exists?(name)
Overlay.cache.has_overlay?(name)
end
#
# Returns the overlay with the specified _name_ from the overlay
# cache. If no such overlay exists in the overlay cache,
# a OverlayNotFound exception will be raised.
#
def Overlay.get(name)
Overlay.cache.get_overlay(name)
end
#
# Installs the Overlay specified by _options_ into the
# Config::REPOSITORY_DIR. If a _block_ is given, it will be
# passed the newly created Overlay after it has been added to
# the Overlay cache.
#
# _options_ must contain the following key:
# :uri:: The URI of the Overlay.
#
# _options_ may contain the following key:
# :media:: The media of the Overlay.
#
def Overlay.install(options={},&block)
options = options.merge(:into => Config::REPOSITORY_DIR)
Repertoire.checkout(options) do |path,media,uri|
return Overlay.add(path,media,uri,&block)
end
end
#
# Adds the Overlay at the specified _path_, the given _uri_
# and given the _uri_ to the Overlay cache. If a _block is given, it
# will be passed the newly created Overlay after it has been added to
# the cache.
#
def Overlay.add(path,media=nil,uri=nil,&block)
Overlay.new(path,media,uri).add(&block)
end
#
# Updates the overlay with the specified _name_. If there is no
# overlay with the specified _name_ in the overlay cache
# a OverlayNotFound exception will be raised. If a _block_ is
# given it will be passed the updated overlay.
#
def Overlay.update(name,&block)
Overlay.get(name).update(&block)
end
#
# Removes the overlay with the specified _name_. If there is no
# overlay with the specified _name_ in the overlay cache
# a OverlayNotFound exception will be raised. If a _block_ is
# given it will be passed the overlay before removal.
#
def Overlay.remove(name,&block)
Overlay.get(name).remove(&block)
end
#
# Uninstall the overlay with the specified _name_. If there is no
# overlay with the specified _name_ in the overlay cache
# a OverlayNotFound exception will be raised. If a _block_ is
# given it will be passed the overlay before uninstalling it.
#
def Overlay.uninstall(name,&block)
Overlay.get(name).uninstall(&block)
end
#
# See OverlayCache#each_overlay.
#
def Overlay.each(&block)
Overlay.cache.each_overlay(&block)
end
#
# See OverlayCache#overlays_with?.
#
def Overlay.with(&block)
Overlay.cache.overlays_with(&block)
end
#
# Returns the overlays which contain the extension with the
# matching _name_.
#
# Overlay.with_extension?('exploits') # => [...]
#
def Overlay.with_extension(name)
Overlay.with { |repo| repo.has_extension?(name) }
end
#
# Returns +true+ if the cache has the extension with the matching
# _name_, returns +false+ otherwise.
#
def Overlay.has_extension?(name)
Overlay.each do |repo|
return true if repo.has_extension?(name)
end
return false
end
#
# Media type of the overlay.
#
def media_type
if @media
return @media.name
else
return nil
end
end
#
# Returns the path to the objects directory of the overlay.
#
def objects_dir
File.expand_path(File.join(@path,OBJECTS_DIR))
end
#
# Caches the objects contained within overlay.
#
def cache_objects
ObjectContext.cache_objects_in(objects_dir)
end
#
# Mirror the objects contained within the overlay.
#
def mirror_objects
ObjectContext.mirror_objects_in(objects_dir)
end
#
# Delete all objects that existed within the overlay.
#
def expunge_objects
ObjectContext.expunge_objects_from(objects_dir)
end
#
# Adds the overlay to the overlay cache. If a _block is given,
# it will be passed the newly created Overlay after it has been
# added to the cache.
#
def add(&block)
Overlay.cache.add(self) do
cache_objects
end
block.call(self) if block
return self
end
#
# Updates the overlay and reloads it's metadata. If a _block_
# is given it will be called after the overlay has been updated.
#
def update(&block)
mirror_objects
if media_type
Repertoire.update(:media => media_type, :path => @path, :uri => @uri)
end
return load_metadata(&block)
end
#
# Removes the overlay from the overlay cache. If a _block_ is
# given, it will be passed the overlay after it has been removed.
#
def remove(&block)
Overlay.cache.remove(self)
expunge_objects
block.call(self) if block
return self
end
#
# Deletes the overlay then removes it from the overlay cache.
# If a _block_ is given, it will be passed the overlay after it
# has been uninstalled.
#
def uninstall(&block)
Repertoire.delete(@path) do
return remove(&block)
end
end
#
# Returns the paths of all extensions within the overlay.
#
def extension_paths
directories
end
#
# Passes each extension path to the specified _block_.
#
def each_extension_path(&block)
extension_paths.each(&block)
end
#
# Returns the names of all extensions within the overlay.
#
def extensions
extension_paths.map { |dir| File.basename(dir) }
end
#
# Passes each extension name to the specified _block_.
#
def each_extension(&block)
extensions.each(&block)
end
#
# Returns +true+ if the overlay contains the extension with the
# specified _name_, returns +false+ otherwise.
#
def has_extension?(name)
extensions.include?(name.to_s)
end
#
# Loads an extension with the specified _name_ from the overlay.
# If a _block_ is given, it will be passed the newly created
# extension.
#
# repo.extension('awesome') # => Extension
#
# repo.extension('shellcode') do |ext|
# ...
# end
#
def extension(name,&block)
name = name.to_s
unless has_extension?(name)
raise(ExtensionNotfound,"overlay #{name.dump} does not contain the extension #{name.dump}",caller)
end
return Extension.load_extension(File.join(@path,name),&block)
end
#
# Returns the +name+ of the Overlay.
#
def to_s
@name.to_s
end
protected
#
# Loads the overlay metadata from the METADATA_FILE within the
# overlay +path+. If a _block_ is given, it will be passed the
# overlay after the metadata has been loaded.
#
def load_metadata(&block)
metadata_path = File.join(@path,METADATA_FILE)
if File.file?(metadata_path)
metadata = REXML::Document.new(open(metadata_path))
metadata.elements.each('/ronin-overlay') do |repo|
@name = repo.elements['name'].get_text.to_s.strip
@license = repo.elements['license'].get_text.to_s.strip
@description = repo.elements['description'].get_text.to_s.strip
end
else
@name = File.basename(@path)
@authors = []
@license = nil
@description = ''
end
block.call(self) if block
return self
end
end
end
end