class PhisherPhinder::MailParser::ReceivedHeaders::Parser

Public Class Methods

new(by_parser:, for_parser:, from_parser:, timestamp_parser:, classifier:) click to toggle source
# File lib/phisher_phinder/mail_parser/received_headers/parser.rb, line 7
def initialize(by_parser:, for_parser:, from_parser:, timestamp_parser:, classifier:)
  @parsers = {
    by: by_parser,
    for: for_parser,
    from: from_parser,
    time: timestamp_parser,
  }
  @classifier = classifier
end

Public Instance Methods

parse(header) click to toggle source
# File lib/phisher_phinder/mail_parser/received_headers/parser.rb, line 17
def parse(header)
  require 'strscan'

  header_value, timestamp = header.split(';')

  parsers = [@by_parser, @for_parser, @from_parser, @starttls_parser, @timestamp_parser]
  components = extract_value_components(header_value).merge(time: timestamp)

  output = @parsers.inject({}) do |memo, (component_name, parser)|
    memo.merge(parser.parse(components[component_name])) do |key, old_value, new_value|
      if key == :starttls && old_value
        old_value
      else
        new_value
      end
    end
  end

  output.merge(@classifier.classify(output))
end

Private Instance Methods

component_start(token, values) click to toggle source
# File lib/phisher_phinder/mail_parser/received_headers/parser.rb, line 71
def component_start(token, values)
  markers = {
    'from' => :from,
    'by' => :by,
    'for' => :for
  }

  return nil unless component = markers[token]

  blocking_components = {
    from: [:by, :for],
    by: [:for],
    for: []
  }

  if blocking_components[component].any? { |blocking_comp| values[blocking_comp].any? }
    nil
  else
    component
  end
end
extract_value_components(header_value) click to toggle source
# File lib/phisher_phinder/mail_parser/received_headers/parser.rb, line 40
def extract_value_components(header_value)

  values = {
    time: [],
    for: [],
    by: [],
    from: []
  }

  cleaned_value = header_value.gsub(/\A\(([^\)]+)\)/, '\1')

  tokenised_value = cleaned_value.split(" ")
  current_component = nil

  tokenised_value.each do |val|
    current_component = component_start(val, values) || current_component

    values[current_component] << val if current_component
  end

  values.inject({}) do |memo, (component, values)|
    memo.merge(component => (values.any? ? values.join(' ') : nil))
  end
end
for_scan(scanner) click to toggle source
# File lib/phisher_phinder/mail_parser/received_headers/parser.rb, line 65
def for_scan(scanner)
  return nil if scanner.eos?

  scanner.check(/for\s<[^>]+>/) ? scanner.scan(/for\s<[^>]+>/) : scanner.scan(/for\s+\S+/)
end