Procházet zdrojové kódy

Add event_headers support to ImapFolderAgent

Akinori MUSHA před 6 roky
rodič
revize
9eb111faa7

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

@@ -5,6 +5,8 @@ require 'mail'
 
 module Agents
   class ImapFolderAgent < Agent
+    include EventHeadersConcern
+
     cannot_receive_events!
 
     can_dry_run!
@@ -55,6 +57,14 @@ module Agents
 
       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-`.
 
       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
 
     IDCACHE_SIZE = 100
@@ -288,6 +298,14 @@ module Agents
             payload['raw_mail'] = Base64.encode64(mail.raw_mail)
           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
 
           notified << mail.message_id if mail.message_id
@@ -413,6 +431,10 @@ module Agents
       "%d %s" % [count, noun.pluralize(count)]
     end
 
+    def event_headers_key
+      super || 'headers'
+    end
+
     class Client < ::Net::IMAP
       class << self
         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
 To: Jane <jane.doe@example.com>, John <john.doe@example.com>
 MIME-Version: 1.0
+X-Foo: test1-1
+X-Foo: test1-2
 Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
 
 --d8c92622e09101e4bc833685557b

+ 2 - 0
spec/data_fixtures/imap2.eml

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

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

@@ -295,6 +295,42 @@ describe Agents::ImapFolderAgent do
         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
         before do
           @checker.options['include_raw_mail'] = true