Browse Source

Add event_headers support to ImapFolderAgent

Akinori MUSHA 6 years ago
parent
commit
9eb111faa7

+ 23 - 1
app/models/agents/imap_folder_agent.rb

@@ -5,6 +5,8 @@ require 'mail'
 
 
 module Agents
 module Agents
   class ImapFolderAgent < Agent
   class ImapFolderAgent < Agent
+    include EventHeadersConcern
+
     cannot_receive_events!
     cannot_receive_events!
 
 
     can_dry_run!
     can_dry_run!
@@ -55,6 +57,14 @@ module Agents
 
 
       Set `mark_as_read` to true to mark found mails as read.
       Set `mark_as_read` to true to mark found mails as read.
 
 
+      Set `event_headers` to a list of header names you want to include in a `headers` hash in each created event, either in an array of string or in a comma-separated string.
+
+      Set `event_headers_style` to one of the following values to normalize the keys of "headers" for downstream agents' convenience:
+
+        * `capitalized` (default) - Header names are capitalized; e.g. "Content-Type"
+        * `downcased` - Header names are downcased; e.g. "content-type"
+        * `snakecased` - Header names are snakecased; e.g. "content_type"
+
       Set `include_raw_mail` to true to add a `raw_mail` value to each created event, which contains a *Base64-encoded* blob in the "RFC822" format defined in [the IMAP4 standard](https://tools.ietf.org/html/rfc3501).  Note that while the result of Base64 encoding will be LF-terminated, its raw content will often be CRLF-terminated because of the nature of the e-mail protocols and formats.  The primary use case for a raw mail blob is to pass to a Shell Command Agent with a command like `openssl enc -d -base64 | tr -d '\r' | procmail -Yf-`.
       Set `include_raw_mail` to true to add a `raw_mail` value to each created event, which contains a *Base64-encoded* blob in the "RFC822" format defined in [the IMAP4 standard](https://tools.ietf.org/html/rfc3501).  Note that while the result of Base64 encoding will be LF-terminated, its raw content will often be CRLF-terminated because of the nature of the e-mail protocols and formats.  The primary use case for a raw mail blob is to pass to a Shell Command Agent with a command like `openssl enc -d -base64 | tr -d '\r' | procmail -Yf-`.
 
 
       Each agent instance memorizes the highest UID of mails that are found in the last run for each watched folder, so even if you change a set of conditions so that it matches mails that are missed previously, or if you alter the flag status of already found mails, they will not show up as new events.
       Each agent instance memorizes the highest UID of mails that are found in the last run for each watched folder, so even if you change a set of conditions so that it matches mails that are missed previously, or if you alter the flag status of already found mails, they will not show up as new events.
@@ -79,7 +89,7 @@ module Agents
             }
             }
           }
           }
 
 
-      Additionally, "raw_mail" will be included if the `include_raw_mail` option is set.
+      Additionally, "headers" will be included if the `event_headers` option is set, and "raw_mail" if the `include_raw_mail` option is set.
     MD
     MD
 
 
     IDCACHE_SIZE = 100
     IDCACHE_SIZE = 100
@@ -288,6 +298,14 @@ module Agents
             payload['raw_mail'] = Base64.encode64(mail.raw_mail)
             payload['raw_mail'] = Base64.encode64(mail.raw_mail)
           end
           end
 
 
+          if interpolated['event_headers'].present?
+            headers = mail.header.each_with_object({}) { |field, hash|
+              name = field.name
+              hash[name] = (v = hash[name]) ? "#{v}\n#{field.value.to_s}" : field.value.to_s
+            }
+            payload.update(event_headers_payload(headers))
+          end
+
           create_event payload: payload
           create_event payload: payload
 
 
           notified << mail.message_id if mail.message_id
           notified << mail.message_id if mail.message_id
@@ -413,6 +431,10 @@ module Agents
       "%d %s" % [count, noun.pluralize(count)]
       "%d %s" % [count, noun.pluralize(count)]
     end
     end
 
 
+    def event_headers_key
+      super || 'headers'
+    end
+
     class Client < ::Net::IMAP
     class Client < ::Net::IMAP
       class << self
       class << self
         def open(host, *args)
         def open(host, *args)

+ 2 - 0
spec/data_fixtures/imap1.eml

@@ -4,6 +4,8 @@ Message-ID: <foo.123@mail.example.jp>
 Subject: some subject
 Subject: some subject
 To: Jane <jane.doe@example.com>, John <john.doe@example.com>
 To: Jane <jane.doe@example.com>, John <john.doe@example.com>
 MIME-Version: 1.0
 MIME-Version: 1.0
+X-Foo: test1-1
+X-Foo: test1-2
 Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
 Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
 
 
 --d8c92622e09101e4bc833685557b
 --d8c92622e09101e4bc833685557b

+ 2 - 0
spec/data_fixtures/imap2.eml

@@ -4,6 +4,8 @@ Message-ID: <bar.456@mail.example.com>
 Subject: Re: some subject
 Subject: Re: some subject
 To: Jane <jane.doe@example.com>, Nanashi <nanashi.gombeh@example.jp>
 To: Jane <jane.doe@example.com>, Nanashi <nanashi.gombeh@example.jp>
 MIME-Version: 1.0
 MIME-Version: 1.0
+X-Foo: test2-1
+X-Foo: test2-2
 Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
 Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
 
 
 --d8c92622e09101e4bc833685557b
 --d8c92622e09101e4bc833685557b

+ 36 - 0
spec/models/agents/imap_folder_agent_spec.rb

@@ -295,6 +295,42 @@ describe Agents::ImapFolderAgent do
         end
         end
       end
       end
 
 
+      describe 'with event_headers' do
+        let(:expected_headers) {
+          [
+            {
+              'mime_version' => '1.0',
+              'x_foo' => "test1-1\ntest1-2"
+            },
+            {
+              'mime_version' => '1.0',
+              'x_foo' => "test2-1\ntest2-2"
+            }
+          ]
+        }
+        before do
+          expected_payloads.zip(expected_headers) do |payload, headers|
+            payload['headers'] = headers
+          end
+
+          @checker.options['event_headers'] = %w[mime-version x-foo]
+          @checker.options['event_headers_style'] = 'snakecased'
+          @checker.save!
+        end
+
+        it 'should check for mails and emit events with headers' do
+          expect { @checker.check }.to change { Event.count }.by(2)
+          expect(@checker.notified.sort).to eq(mails.map(&:message_id).sort)
+          expect(@checker.lastseen).to eq(mails.each_with_object(@checker.make_seen) { |mail, seen|
+              seen[mail.uidvalidity] = mail.uid
+            })
+
+          expect(Event.last(2).map(&:payload)).to match expected_payloads
+
+          expect { @checker.check }.not_to change { Event.count }
+        end
+      end
+
       describe 'with include_raw_mail' do
       describe 'with include_raw_mail' do
         before do
         before do
           @checker.options['include_raw_mail'] = true
           @checker.options['include_raw_mail'] = true