Class: MailAuth::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/mailauth/message.rb

Overview

A received message, split once into the parts every check needs. Binary throughout: DKIM hashes bytes, so transcoding changes the answer.

Constant Summary collapse

HEADER_SEPARATOR =
"\r\n\r\n".b

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw) ⇒ Message

Returns a new instance of Message.



9
10
11
12
13
14
# File 'lib/mailauth/message.rb', line 9

def initialize(raw)
  @raw = normalize(raw)
  @headers, @body = @raw.split(HEADER_SEPARATOR, 2)
  @headers = @headers.to_s
  @body = @body.to_s
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



7
8
9
# File 'lib/mailauth/message.rb', line 7

def body
  @body
end

#headersObject (readonly)

Returns the value of attribute headers.



7
8
9
# File 'lib/mailauth/message.rb', line 7

def headers
  @headers
end

#rawObject (readonly)

Returns the value of attribute raw.



7
8
9
# File 'lib/mailauth/message.rb', line 7

def raw
  @raw
end

Instance Method Details

#field_name(field) ⇒ Object



31
32
33
# File 'lib/mailauth/message.rb', line 31

def field_name(field)
  field.split(":", 2).first.to_s.strip.downcase
end

#fieldsObject

Order is load-bearing: DKIM walks duplicate headers from the bottom up.



17
18
19
20
21
22
23
24
25
# File 'lib/mailauth/message.rb', line 17

def fields
  @fields ||= headers.lines.each_with_object([]) do |line, fields|
    if fields.any? && line.start_with?(" ", "\t")
      fields.last << line
    else
      fields << line
    end
  end
end

#fields_named(name) ⇒ Object



27
28
29
# File 'lib/mailauth/message.rb', line 27

def fields_named(name)
  fields.select { |field| field_name(field) == name.downcase }
end

#header_from_domainObject

What DMARC alignment is measured against. Quoted display names are stripped first: From: "Foo <evil@attacker.example>" <real@bank.example> renders as bank.example, and taking the first address found would align against the attacker's domain instead. Several From addresses (RFC 5322 permits it; DMARC doesn't) is no domain rather than a guess.



40
41
42
43
44
45
46
# File 'lib/mailauth/message.rb', line 40

def header_from_domain
  addresses = from_addresses

  if addresses.one?
    addresses.first.downcase.chomp(".")
  end
end