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|