class LogStash::Inputs::IMAP

Read mail from IMAP servers

Periodically scans INBOX and moves any read messages to the trash.

Constants

ISO8601_STRFTIME

Public Instance Methods

check_mail(queue) click to toggle source
# File lib/logstash/inputs/imap.rb, line 60
def check_mail(queue)
  # TODO(sissel): handle exceptions happening during runtime:
  # EOFError, OpenSSL::SSL::SSLError
  imap = connect
  imap.select("INBOX")
  ids = imap.search("NOT SEEN")

  ids.each_slice(@fetch_count) do |id_set|
    items = imap.fetch(id_set, "RFC822")
    items.each do |item|
      next unless item.attr.has_key?("RFC822")
      mail = Mail.read_from_string(item.attr["RFC822"])
      queue << parse_mail(mail)
    end

    imap.store(id_set, '+FLAGS', @delete ? :Deleted : :Seen)
  end

  imap.close
  imap.disconnect
end
connect() click to toggle source
# File lib/logstash/inputs/imap.rb, line 48
def connect
  imap = Net::IMAP.new(@host, :port => @port, :ssl => @secure)
  imap.login(@user, @password.value)
  return imap
end
parse_mail(mail) click to toggle source
# File lib/logstash/inputs/imap.rb, line 82
def parse_mail(mail)
  # TODO(sissel): What should a multipart message look like as an event?
  # For now, just take the plain-text part and set it as the message.
  if mail.parts.count == 0
    # No multipart message, just use the body as the event text
    message = mail.body.decoded
  else
    # Multipart message; use the first text/plain part we find
    part = mail.parts.find { |p| p.content_type.match @content_type_re } || mail.parts.first
    message = part.decoded
  end

  event = LogStash::Event.new("message" => message)

  # Use the 'Date' field as the timestamp
  event["@timestamp"] = mail.date.to_time.gmtime

  # Add fields: Add message.header_fields { |h| h.name=> h.value }
  mail.header_fields.each do |header|
    if @lowercase_headers
      # 'header.name' can sometimes be a Mail::Multibyte::Chars, get it in
      # String form
      name = header.name.to_s.downcase
    else
      name = header.name.to_s
    end
    # Call .decoded on the header in case it's in encoded-word form.
    # Details at:
    #   https://github.com/mikel/mail/blob/master/README.md#encodings
    #   http://tools.ietf.org/html/rfc2047#section-2
    value = header.decoded

    # Assume we already processed the 'date' above.
    next if name == "Date"

    case event[name]
      # promote string to array if a header appears multiple times
      # (like 'received')
      when String; event[name] = [event[name], value]
      when Array; event[name].is_a?(Array)
      when nil; event[name] = value
    end
  end # mail.header_fields.each

  decorate(event)

  return event
end
register() click to toggle source
# File lib/logstash/inputs/imap.rb, line 33
def register
  require "net/imap" # in stdlib
  require "mail" # gem 'mail'

  if @port.nil?
    if @secure
      @port = 993
    else
      @port = 143
    end
  end

  @content_type_re = Regexp.new("^" + @content_type)
end
run(queue) click to toggle source
# File lib/logstash/inputs/imap.rb, line 54
def run(queue)
  Stud.interval(@check_interval) do
    check_mail(queue)
  end
end
teardown() click to toggle source
# File lib/logstash/inputs/imap.rb, line 132
def teardown
  $stdin.close
  finished
end