lib/spf/model.rb in spf-0.0.13 vs lib/spf/model.rb in spf-0.0.14
- old
+ new
@@ -92,15 +92,16 @@
@ip_network = nil
@ipv4_prefix_length = nil
@ipv6_prefix_length = nil
@errors = []
@ip_netblocks = []
+ @raise_exceptions = options.has_key?(:raise_exceptions) ? options[:raise_exceptions] : true
end
def error(exception)
+ raise exception if @raise_exceptions
@errors << exception
- raise exception
end
def self.new_from_string(text, options = {})
#term = SPF::Term.new(options, {:text => text})
options[:text] = text
@@ -113,75 +114,79 @@
if @parse_text.sub!(/^(#{DOMAIN_SPEC_PATTERN})/x, '')
domain_spec = $1
domain_spec.sub!(/^(.*?)\.?$/, $1)
@domain_spec = SPF::MacroString.new({:text => domain_spec})
elsif required
- raise SPF::TermDomainSpecExpectedError.new(
- "Missing required domain-spec in '#{@text}'")
+ error(SPF::TermDomainSpecExpectedError.new(
+ "Missing required domain-spec in '#{@text}'"))
end
end
def parse_ipv4_address(required = false)
if @parse_text.sub!(/^(#{IPV4_ADDRESS_PATTERN})/x, '')
@ip_address = $1
elsif required
- raise SPF::TermIPv4AddressExpectedError.new(
- "Missing required IPv4 address in '#{@text}'")
+ error(SPF::TermIPv4AddressExpectedError.new(
+ "Missing or invalid required IPv4 address in '#{@text}'"))
end
end
def parse_ipv4_prefix_length(required = false)
if @parse_text.sub!(/^\/(\d+)/, '')
bits = $1.to_i
unless bits and bits >= 0 and bits <= 32 and $1 !~ /^0./
- raise SPF::TermIPv4PrefixLengthExpected.new(
- "Invalid IPv4 prefix length encountered in '#{@text}'")
+ error(SPF::TermIPv4PrefixLengthExpected.new(
+ "Invalid IPv4 prefix length encountered in '#{@text}'"))
+ return
end
@ipv4_prefix_length = bits
elsif required
- raise SPF::TermIPv4PrefixLengthExpected.new(
- "Missing required IPv4 prefix length in '#{@text}")
+ error(SPF::TermIPv4PrefixLengthExpected.new(
+ "Missing required IPv4 prefix length in '#{@text}"))
+ return
else
@ipv4_prefix_length = self.default_ipv4_prefix_length
end
end
def parse_ipv4_network(required = false)
self.parse_ipv4_address(required)
self.parse_ipv4_prefix_length
- @ip_network = IP.new("#{@ip_address}/#{@ipv4_prefix_length}")
+ @ip_network = IP.new("#{@ip_address}/#{@ipv4_prefix_length}") if @ip_address and @ipv4_prefix_length
end
def parse_ipv6_address(required = false)
if @parse_text.sub!(/(#{IPV6_ADDRESS_PATTERN})(?=\/|$)/x, '')
@ip_address = $1
elsif required
- raise SPF::TermIPv6AddressExpected.new(
- "Missing required IPv6 address in '#{@text}'")
+ error(SPF::TermIPv6AddressExpected.new(
+ "Missing required IPv6 address in '#{@text}'"))
end
end
def parse_ipv6_prefix_length(required = false)
if @parse_text.sub!(/^\/(\d+)/, '')
bits = $1.to_i
unless bits and bits >= 0 and bits <= 128 and $1 !~ /^0./
- raise SPF::TermIPv6PrefixLengthExpectedError.new(
- "Invalid IPv6 prefix length encountered in '#{@text}'")
+ error(SPF::TermIPv6PrefixLengthExpectedError.new(
+ "Invalid IPv6 prefix length encountered in '#{@text}'"))
+ return
end
@ipv6_prefix_length = bits
elsif required
- raise SPF::TermIPvPrefixLengthExpected.new(
- "Missing required IPv6 prefix length in '#{@text}'")
+ error(SPF::TermIPvPrefixLengthExpected.new(
+ "Missing required IPv6 prefix length in '#{@text}'"))
+ return
else
@ipv6_prefix_length = self.default_ipv6_prefix_length
end
end
def parse_ipv6_network(required = false)
self.parse_ipv6_address(required)
self.parse_ipv6_prefix_length
- @ip_network = IP.new("#{@ip_address}/#{@ipv6_prefix_length}")
+ @ip_network = IP.new("#{@ip_address}/#{@ipv6_prefix_length}") if @ip_address and @ipv6_prefix_length
end
def parse_ipv4_ipv6_prefix_lengths
self.parse_ipv4_prefix_length
if self.instance_variable_defined?(:@ipv4_prefix_length) and # An IPv4 prefix length has been parsed, and
@@ -203,11 +208,13 @@
return @text
else
raise SPF::NoUnparsedTextError
end
end
+end
+class SPF::UnknownTerm < SPF::Term
end
class SPF::Mech < SPF::Term
DEFAULT_QUALIFIER = SPF::Record::DEFAULT_QUALIFIER
@@ -240,29 +247,29 @@
def parse
if not @parse_text
raise SPF::NothingToParseError.new('Nothing to parse for mechanism')
end
parse_qualifier
- parse_name
- parse_params
- parse_end
+ parse_name if @errors.empty?
+ parse_params if @errors.empty?
+ parse_end if @errors.empty?
end
def parse_qualifier
if @parse_text.sub!(/(#{QUALIFIER_PATTERN})?/x, '')
@qualifier = $1 or DEFAULT_QUALIFIER
else
- raise SPF::InvalidMechQualifierError.new(
- "Invalid qualifier encountered in '#{@text}'")
+ error(SPF::InvalidMechQualifierError.new(
+ "Invalid qualifier encountered in '#{@text}'"))
end
end
def parse_name
if @parse_text.sub!(/^ (#{NAME_PATTERN}) (?: : (?=.) )? /x, '')
@name = $1
else
- raise SPF::InvalidMechError.new("Unexpected mechanism encountered in '#{@text}'")
+ error(SPF::InvalidMechError.new("Unexpected mechanism encountered in '#{@text}'"))
end
end
def parse_params(required = true)
# Parse generic string of parameters text (should be overridden in sub-classes):
@@ -271,11 +278,11 @@
end
end
def parse_end
unless @parse_text == ''
- raise SPF::JunkInTermError.new("Junk encountered in mechanism '#{@text}'")
+ error(SPF::JunkInTermError.new("Junk encountered in mechanism '#{@text}'"))
end
@parse_text = nil
end
def qualifier
@@ -430,10 +437,11 @@
end
return result
end
def match(server, request, want_result = true)
+ return false unless @ip_network
ip_network_v6 = IP::V4 === @ip_network ?
SPF::Util.ipv4_address_to_ipv6(@ip_network) :
@ip_network
return ip_network_v6.contains?(request.ip_address_v6)
end
@@ -605,40 +613,40 @@
@domain_spec = SPF::MacroString.new({:text => @domain_spec})
end
end
def parse
- raise SPF::NothingToParseError('Nothing to parse for modifier') unless @parse_text
- self.parse_name
- self.parse_params(true)
- self.parse_end
+ error(SPF::NothingToParseError('Nothing to parse for modifier')) unless @parse_text
+ self.parse_name if @errors.empty?
+ self.parse_params(true) if @errors.empty?
+ self.parse_end if @errors.empty?
end
def parse_name
@parse_text.sub!(/^(#{self.class::NAME})=/i, '')
if $1
@name = $1
else
- raise SPF::InvalidModError.new(
- "Unexpected modifier name encoutered in #{@text}")
+ error(SPF::InvalidModError.new(
+ "Unexpected modifier name encoutered in #{@text}"))
end
end
def parse_params(required = false)
# Parse generic macro string of parameters text (should be overridden in sub-classes):
@parse_text.sub!(/^(#{MACRO_STRING_PATTERN})$/x, '')
if $1
@params_text = $1
elsif required
- raise SPF::InvalidMacroStringError.new(
- "Invalid macro string encountered in #{@text}")
+ error(SPF::InvalidMacroStringError.new(
+ "Invalid macro string encountered in #{@text}"))
end
end
def parse_end
unless @parse_text == ''
- raise SPF::JunkInTermError("Junk encountered in modifier #{@text}")
+ error(SPF::JunkInTermError.new("Junk encountered in modifier #{@text}"))
end
@parse_text = nil
end
def to_s
@@ -782,18 +790,24 @@
record = new(options)
record.parse
return record
end
+ def error(exception)
+ raise exception if @raise_exceptions
+ @errors << exception
+ end
+
def ip_netblocks
@ip_netblocks.flatten!
return @ip_netblocks
end
def parse
unless self.instance_variable_defined?(:@parse_text) and @parse_text
- raise SPF::NothingToParseError.new('Nothing to parse for record')
+ error(SPF::NothingToParseError.new('Nothing to parse for record'))
+ return
end
self.parse_version_tag
while @parse_text.length > 0
term = nil
begin
@@ -803,15 +817,13 @@
@errors << e
raise if @raise_exceptions
return if SPF::JunkInRecordError === e
end
end
- #self.parse_end
end
def parse_version_tag
- #@parse_text.sub!(self.version_tag_pattern, '')
@parse_text.sub!(/^#{self.version_tag_pattern}\s+/ix, '')
unless $1
raise SPF::InvalidRecordVersionError.new(
"Not a '#{self.version_tag}' record: '#{@text}'")
end
@@ -831,13 +843,20 @@
term = nil
if @parse_text.sub!(regex, '') and $&
# Looks like a mechanism:
mech_text = $1
mech_name = $2.downcase
- mech_class = self.mech_classes[mech_name.to_sym] || SPF::Mech
- term = mech = mech_class.new_from_string(mech_text)
- @ip_netblocks << mech.ip_netblocks
+ mech_class = self.mech_classes[mech_name.to_sym]
+ exception = nil
+ unless mech_class
+ exception = SPF::InvalidMech.new("Unknown mechanism type '#{mech_name}' in '#{@version_tag}' record")
+ error(exception)
+ mech_class = SPF::Mech
+ end
+ term = mech = mech_class.new_from_string(mech_text, {:raise_exceptions => @raise_exceptions})
+ term.errors << exception if exception
+ @ip_netblocks << mech.ip_netblocks if mech.ip_netblocks
@terms << mech
if mech_class == SPF::Mech
raise SPF::InvalidMechError.new("Unknown mechanism type '#{mech_name}' in '#{@version_tag}' record")
end
elsif (
@@ -854,11 +873,11 @@
mod_text = $1
mod_name = $2.downcase
mod_class = self.class::MOD_CLASSES[mod_name.to_sym] || SPF::Mod
if mod_class
# Known modifier.
- term = mod = mod_class.new_from_string(mod_text)
+ term = mod = mod_class.new_from_string(mod_text, {:raise_exceptions => @raise_exceptions})
if SPF::GlobalMod === mod
# Global modifier.
if @global_mods[mod_name]
raise SPF::DuplicateGlobalMod.new("Duplicate global modifier '#{mod_name}' encountered")
end
@@ -870,10 +889,11 @@
end
else
raise SPF::JunkInRecordError.new("Junk encountered in record '#{@text}'")
end
+ @errors.concat(term.errors)
return term
end
def global_mods
return @global_mods.values.sort {|a,b| a.precedence <=> b.precedence }
@@ -908,10 +928,10 @@
mod.process(server, request)
elsif SPF::UnknownMod === term
# Term is an unknown modifier. Ignore it (RFC 4408, 6/3).
else
# Invalid term object encountered:
- raise SPF::UnexpectedTermObjectError.new("Unexpected term object '#{term}' encountered.")
+ error(SPF::UnexpectedTermObjectError.new("Unexpected term object '#{term}' encountered."))
end
end
rescue SPF::Result => result
# Process global modifiers in ascending order of precedence:
@global_mods.each do |global_mod|