
# :title: PacketFu Documentation
# :main: README

cwd = File.expand_path(File.dirname(__FILE__))

$: << cwd

require File.join(cwd,"packetfu","structfu")
require "ipaddr"
require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/

module PacketFu

	# Picks up all the protocols defined in the protos subdirectory
	def self.require_protos(cwd)
		protos_dir = File.join(cwd, "packetfu", "protos")
		Dir.new(protos_dir).each do |fname|
			next unless fname[/\.rb$/]
			begin 
				require File.join(protos_dir,fname)
			rescue
				warn "Warning: Could not load `#{fname}'. Skipping."
			end
		end
	end

	# Deal with Ruby's encoding by ignoring it.
	def self.force_binary(str)
		str.force_encoding Encoding::BINARY if str.respond_to? :force_encoding
	end

	# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
	@byte_order = :little

	# Checks if pcaprub is loaded correctly.
	@pcaprub_loaded = false

	# PacketFu works best with Pcaprub version 0.8-dev (at least)
	# The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
	def self.pcaprub_platform_require
		begin
			require 'pcaprub'
		rescue LoadError
			return false
		end
		@pcaprub_loaded = true 
	end

	pcaprub_platform_require

	if @pcaprub_loaded
		pcaprub_regex = /[0-9]\.([8-9]|[1-7][0-9])(-dev)?/ # Regex for 0.8 and beyond.
		if Pcap.version !~ pcaprub_regex 
			@pcaprub_loaded = false # Don't bother with broken versions
			raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
		end
		require "packetfu/capture" 
		require "packetfu/inject"
	end

	# Returns the status of pcaprub
	def self.pcaprub_loaded?
		@pcaprub_loaded
	end

	# Returns an array of classes defined in PacketFu
	def self.classes
		constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
	end

	# Adds the class to PacketFu's list of packet classes -- used in packet parsing.
	def self.add_packet_class(klass)
		raise "Need a class" unless klass.kind_of? Class
		if klass.name !~ /[A-Za-z0-9]Packet/
			raise "Packet classes should be named 'ProtoPacket'"
		end
		@packet_classes ||= []
		@packet_classes << klass
		@packet_classes_dirty = true
		@packet_classes.sort! {|x,y| x.name <=> y.name}
	end

	# Presumably, there may be a time where you'd like to remove a packet class.
	def self.remove_packet_class(klass)
		raise "Need a class" unless klass.kind_of? Class
		@packet_classes ||= []
		@packet_classes.delete klass
		@packet_classes_dirty = true
		@packet_classes 
	end

	# Returns an array of packet classes
	def self.packet_classes
		@packet_classes || []
	end

	# Returns an array of packet types by packet prefix.
	def self.packet_prefixes
		return [] if @packet_classes.nil?
		return @packet_class_prefixes if @packet_classes_dirty == false
		@packet_classes_dirty = false
		@packet_class_prefixes = @packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
		return @packet_class_prefixes
	end

	# The current inspect style. One of :hex, :dissect, or :default
	# Note that :default means Ruby's default, which is usually
	# far too long to be useful.
	def self.inspect_style
		@inspect_style ||= :dissect
	end

	# Setter for PacketFu's @inspect_style
	def self.inspect_style=(arg)
		@inspect_style = case arg
			when :hex, :pretty
				:hex
			when :dissect, :verbose
				:dissect
			when :default, :ugly
				:default
			else
				:dissect
			end
	end

	# Switches inspect styles in a round-robin fashion between 
	# :dissect, :default, and :hex
	def toggle_inspect
		case @inspect_style
		when :hex, :pretty
			@inspect_style = :dissect
		when :dissect, :verbose
			@inspect_style = :default
		when :default, :ugly
			@inspect_style = :hex
		else
			@inspect_style = :dissect
		end
	end


end

require File.join(cwd,"packetfu","version")
require File.join(cwd,"packetfu","pcap")
require File.join(cwd,"packetfu","packet")
PacketFu.require_protos(cwd)
require File.join(cwd,"packetfu","utils")
require File.join(cwd,"packetfu","config")

# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
