Class: SPF::Record

Inherits:
Object
  • Object
show all
Defined in:
lib/spf/model.rb,
lib/spf/model.rb

Direct Known Subclasses

V1, V2

Defined Under Namespace

Classes: V1, V2

Constant Summary collapse

DEFAULT_QUALIFIER =
'+'
RESULTS_BY_QUALIFIER =
{
  ''  => :pass,
  '+' => :pass,
  '-' => :fail,
  '~' => :softfail,
  '?' => :neutral
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Record

Returns a new instance of Record.



770
771
772
773
774
775
776
777
778
# File 'lib/spf/model.rb', line 770

def initialize(options)
  super()
  @parse_text       = (@text = options[:text] if not self.instance_variable_defined?(:@parse_text)).dup
  @terms          ||= []
  @global_mods    ||= {}
  @errors           = []
  @ip_netblocks     = []
  @raise_exceptions = options.has_key?(:raise_exceptions) ? options[:raise_exceptions] : true
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



760
761
762
# File 'lib/spf/model.rb', line 760

def errors
  @errors
end

#termsObject (readonly)

Returns the value of attribute terms.



760
761
762
# File 'lib/spf/model.rb', line 760

def terms
  @terms
end

#textObject (readonly)

Returns the value of attribute text.



760
761
762
# File 'lib/spf/model.rb', line 760

def text
  @text
end

Class Method Details

.new_from_string(text, options = {}) ⇒ Object



780
781
782
783
784
785
# File 'lib/spf/model.rb', line 780

def self.new_from_string(text, options = {})
  options[:text] = text
  record = new(options)
  record.parse
  return record
end

Instance Method Details

#eval(server, request, want_result = true) ⇒ Object



890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
# File 'lib/spf/model.rb', line 890

def eval(server, request, want_result = true)
  raise SPF::OptionRequiredError.new('SPF server object required for record evaluation') unless server
  raise SPF::OptionRequiredError.new('Request object required for record evaluation')    unless request
  begin
    @terms.each do |term|
      if SPF::Mech === term
        # Term is a mechanism.
        mech = term
        if mech.match(server, request, request.ip_address != nil)
          result_name = RESULTS_BY_QUALIFIER[mech.qualifier]
          result_class = server.result_class(result_name)
          result = result_class.new([server, request, "Mechanism '#{term}' matched"])
          mech.explain(server, request, result)
          raise result if want_result
        end
      elsif SPF::PositionalMod === term
        # Term is a positional modifier.
        mod = term
        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.")
      end
    end
  rescue SPF::Result => result
    # Process global modifiers in ascending order of precedence:
    @global_mods.each do |global_mod|
      global_mod.process(server, request, result)
    end
    raise result if want_result
  end
end

#global_mod(mod_name) ⇒ Object



882
883
884
# File 'lib/spf/model.rb', line 882

def global_mod(mod_name)
  return @global_mods[mod_name]
end

#global_modsObject



878
879
880
# File 'lib/spf/model.rb', line 878

def global_mods
  return @global_mods.values.sort {|a,b| a.precedence <=> b.precedence }
end

#ip_netblocksObject



787
788
789
790
# File 'lib/spf/model.rb', line 787

def ip_netblocks
  @ip_netblocks.flatten!
  return @ip_netblocks
end

#parseObject



792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
# File 'lib/spf/model.rb', line 792

def parse
  unless self.instance_variable_defined?(:@parse_text) and @parse_text
    raise SPF::NothingToParseError.new('Nothing to parse for record')
  end
  self.parse_version_tag
  while @parse_text.length > 0
    term = nil
    begin
      term = self.parse_term
    rescue SPF::Error => e
      term.errors << e if term
      @errors     << e
      raise if @raise_exceptions
      return if SPF::JunkInRecordError === e
    end
  end
  #self.parse_end
end

#parse_termObject



820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
# File 'lib/spf/model.rb', line 820

def parse_term
  regex = /
    ^
    (
      #{SPF::Mech::QUALIFIER_PATTERN}?
      (#{SPF::Mech::NAME_PATTERN})
      [^\x20]*
    )
    (?: \x20+ | $ )
  /x

  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
    @terms << mech
    if mech_class == SPF::Mech
      raise SPF::InvalidMechError.new("Unknown mechanism type '#{mech_name}' in '#{@version_tag}' record")
    end
  elsif (
    @parse_text.sub!(/
      ^
      (
        (#{SPF::Mod::NAME_PATTERN}) =
        [^\x20]*
      )
      (?: \x20+ | $ )
    /x, '') and $&
  )
    # Looks like a modifier:
    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)
      if SPF::GlobalMod === mod
        # Global modifier.
        if @global_mods[mod_name]
          raise SPF::DuplicateGlobalMod.new("Duplicate global modifier '#{mod_name}' encountered")
        end
        @global_mods[mod_name] = mod
      elsif SPF::PositionalMod === mod
        # Positional modifier, queue normally:
        @terms << mod
      end
    end

  else
    raise SPF::JunkInRecordError.new("Junk encountered in record '#{@text}'")
  end
  return term
end

#parse_version_tagObject



811
812
813
814
815
816
817
818
# File 'lib/spf/model.rb', line 811

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
end

#to_sObject



886
887
888
# File 'lib/spf/model.rb', line 886

def to_s
  return [version_tag, @terms, @global_mods].join(' ')
end