Class: MailerToGo::SPF::Result

Inherits:
Struct
  • Object
show all
Defined in:
lib/mailertogo/spf/result.rb

Overview

The answer to "does this domain's published SPF authorise me?", shaped so callers ask it questions rather than pattern-match a hash.

status — :pass | :pinned | :fail | :permerror | :unknown

:pass      — the sender is reachable from the record, directly or
           through its include chain: durable authorisation.
:pinned    — no include chain to the sender, but the record hardcodes
           the sender's current sending addresses. Mail passes SPF
           *today* and breaks silently the moment an address moves,
           so this is deliberately NOT :pass. See #pinned?.
:fail      — the chain resolved fine; the sender simply is not in it.
:permerror — the record is broken (duplicate records, or past the
           §4.6.4 lookup cap). Receivers reject it, so nothing passes.
:unknown   — DNS did not answer. Verdict withheld, NOT a failure.

The four-valued status exists so a resolver hiccup can never be mistaken for "this domain removed my record". Anything that gates a customer on SPF has to be able to tell those apart.

reason narrows :permerror (:duplicate_records | :lookup_limit) and :pinned (:pinned_full | :pinned_partial).

all_qualifier (:pass | :fail | :softfail | :neutral | nil) is what the record tells receivers to do with mail it does NOT authorise — the qualifier on its terminal all (RFC 7208 §5.1). It is deliberately a FIELD rather than a fifth status: the yes/no decision ("is my include published?") is identical either way, but a ~all domain's unauthorised mail is merely marked while a -all domain's is rejected outright, and the customer's remedy is far more urgent in the second case. Callers that write to humans need to say which one they published.

partial is true when part of the chain did not resolve, which makes lookups a FLOOR rather than the real cost. Harmless for the verdict — a match is a match — but it matters to anyone pricing a record against the §4.6.4 budget, who must not report "this fits" from a count it could not finish.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#all_qualifierObject

Returns the value of attribute all_qualifier

Returns:

  • (Object)

    the current value of all_qualifier



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def all_qualifier
  @all_qualifier
end

#detailObject

Returns the value of attribute detail

Returns:

  • (Object)

    the current value of detail



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def detail
  @detail
end

#lookupsObject

Returns the value of attribute lookups

Returns:

  • (Object)

    the current value of lookups



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def lookups
  @lookups
end

#matchedObject

Returns the value of attribute matched

Returns:

  • (Object)

    the current value of matched



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def matched
  @matched
end

#partialObject

Returns the value of attribute partial

Returns:

  • (Object)

    the current value of partial



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def partial
  @partial
end

#reasonObject

Returns the value of attribute reason

Returns:

  • (Object)

    the current value of reason



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def reason
  @reason
end

#statusObject

Returns the value of attribute status

Returns:

  • (Object)

    the current value of status



42
43
44
# File 'lib/mailertogo/spf/result.rb', line 42

def status
  @status
end

Instance Method Details

#defectObject

Which defect to tell the customer about, or nil when the record is clean: :ip_pinned | :lookup_limit | :duplicate_records.



64
65
66
67
68
69
# File 'lib/mailertogo/spf/result.rb', line 64

def defect
  case status
  when :pinned then :ip_pinned
  when :permerror then reason == :lookup_limit ? :lookup_limit : :duplicate_records
  end
end

#failed?Boolean

Returns:

  • (Boolean)


48
# File 'lib/mailertogo/spf/result.rb', line 48

def failed? = %i[fail permerror].include?(status)

#partial?Boolean

Returns:

  • (Boolean)


49
# File 'lib/mailertogo/spf/result.rb', line 49

def partial? = partial == true

#pass?Boolean

Returns:

  • (Boolean)


44
# File 'lib/mailertogo/spf/result.rb', line 44

def pass? = status == :pass

#permerror?Boolean

Returns:

  • (Boolean)


46
# File 'lib/mailertogo/spf/result.rb', line 46

def permerror? = status == :permerror

#permerror_with_sender_published?Boolean

The record IS broken for receivers, but the sender's include is sitting in it. Gating callers use this to avoid treating "the customer's SPF has an unrelated RFC problem" as "the customer removed my record" — the remedy is completely different, and un-verifying them is wrong.

Returns:

  • (Boolean)


60
# File 'lib/mailertogo/spf/result.rb', line 60

def permerror_with_sender_published? = permerror? && !matched.to_s.empty?

#pinned?Boolean

Returns:

  • (Boolean)


45
# File 'lib/mailertogo/spf/result.rb', line 45

def pinned? = status == :pinned

#softfail?Boolean

Unauthorised, but under a ~all: receivers mark the mail rather than rejecting it, so the domain is in a materially better position than a -all :fail. Same defect, different urgency.

Returns:

  • (Boolean)


54
# File 'lib/mailertogo/spf/result.rb', line 54

def softfail? = status == :fail && all_qualifier == :softfail

#unknown?Boolean

Returns:

  • (Boolean)


47
# File 'lib/mailertogo/spf/result.rb', line 47

def unknown? = status == :unknown