actionmailbox 0.1.0 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/{LICENSE → MIT-LICENSE} +1 -1
  4. data/README.md +2 -267
  5. data/app/controllers/action_mailbox/base_controller.rb +26 -31
  6. data/app/controllers/action_mailbox/ingresses/amazon/inbound_emails_controller.rb +48 -44
  7. data/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb +88 -84
  8. data/app/controllers/action_mailbox/ingresses/mandrill/inbound_emails_controller.rb +64 -60
  9. data/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb +62 -0
  10. data/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb +65 -0
  11. data/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb +51 -47
  12. data/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +27 -20
  13. data/app/controllers/rails/conductor/action_mailbox/reroutes_controller.rb +15 -11
  14. data/app/controllers/rails/conductor/base_controller.rb +12 -8
  15. data/app/jobs/action_mailbox/incineration_job.rb +17 -13
  16. data/app/jobs/action_mailbox/routing_job.rb +10 -6
  17. data/app/models/action_mailbox/inbound_email.rb +43 -37
  18. data/app/models/action_mailbox/inbound_email/incineratable.rb +5 -3
  19. data/app/models/action_mailbox/inbound_email/incineratable/incineration.rb +21 -17
  20. data/app/models/action_mailbox/inbound_email/message_id.rb +22 -20
  21. data/app/models/action_mailbox/inbound_email/routable.rb +7 -5
  22. data/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb +5 -0
  23. data/config/routes.rb +2 -1
  24. data/db/migrate/20180917164000_create_action_mailbox_tables.rb +10 -4
  25. data/lib/action_mailbox.rb +2 -1
  26. data/lib/action_mailbox/base.rb +100 -93
  27. data/lib/action_mailbox/callbacks.rb +3 -1
  28. data/lib/action_mailbox/engine.rb +9 -1
  29. data/lib/action_mailbox/gem_version.rb +17 -0
  30. data/lib/action_mailbox/mail_ext.rb +2 -0
  31. data/lib/action_mailbox/mail_ext/address_equality.rb +7 -3
  32. data/lib/action_mailbox/mail_ext/address_wrapping.rb +7 -3
  33. data/lib/action_mailbox/mail_ext/addresses.rb +22 -18
  34. data/lib/action_mailbox/mail_ext/from_source.rb +2 -0
  35. data/lib/action_mailbox/mail_ext/recipients.rb +7 -3
  36. data/lib/action_mailbox/{postfix_relayer.rb → relayer.rb} +18 -10
  37. data/lib/action_mailbox/router.rb +30 -26
  38. data/lib/action_mailbox/router/route.rb +34 -30
  39. data/lib/action_mailbox/routing.rb +3 -1
  40. data/lib/action_mailbox/test_case.rb +4 -0
  41. data/lib/action_mailbox/test_helper.rb +8 -6
  42. data/lib/action_mailbox/version.rb +8 -1
  43. data/lib/rails/generators/installer.rb +10 -0
  44. data/lib/rails/generators/mailbox/USAGE +12 -0
  45. data/lib/rails/generators/mailbox/mailbox_generator.rb +32 -0
  46. data/lib/{templates/mailboxes/application_mailbox.rb → rails/generators/mailbox/templates/application_mailbox.rb.tt} +1 -1
  47. data/lib/rails/generators/mailbox/templates/mailbox.rb.tt +4 -0
  48. data/lib/rails/generators/test_unit/mailbox_generator.rb +20 -0
  49. data/lib/rails/generators/test_unit/templates/mailbox_test.rb.tt +13 -0
  50. data/lib/tasks/ingress.rake +53 -5
  51. data/lib/tasks/install.rake +1 -1
  52. metadata +66 -237
  53. data/.gitignore +0 -2
  54. data/Gemfile +0 -8
  55. data/Gemfile.lock +0 -159
  56. data/Rakefile +0 -27
  57. data/actionmailbox.gemspec +0 -27
  58. data/app/controllers/action_mailbox/ingresses/postfix/inbound_emails_controller.rb +0 -55
  59. data/bin/test +0 -5
  60. data/lib/templates/installer.rb +0 -4
  61. data/test/controllers/ingresses/amazon/inbound_emails_controller_test.rb +0 -20
  62. data/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb +0 -89
  63. data/test/controllers/ingresses/mandrill/inbound_emails_controller_test.rb +0 -58
  64. data/test/controllers/ingresses/postfix/inbound_emails_controller_test.rb +0 -54
  65. data/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb +0 -44
  66. data/test/dummy/.babelrc +0 -18
  67. data/test/dummy/.gitignore +0 -3
  68. data/test/dummy/.postcssrc.yml +0 -3
  69. data/test/dummy/Rakefile +0 -6
  70. data/test/dummy/app/assets/config/manifest.js +0 -3
  71. data/test/dummy/app/assets/images/.keep +0 -0
  72. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  73. data/test/dummy/app/assets/stylesheets/scaffold.css +0 -80
  74. data/test/dummy/app/channels/application_cable/channel.rb +0 -4
  75. data/test/dummy/app/channels/application_cable/connection.rb +0 -4
  76. data/test/dummy/app/controllers/application_controller.rb +0 -2
  77. data/test/dummy/app/controllers/concerns/.keep +0 -0
  78. data/test/dummy/app/helpers/application_helper.rb +0 -2
  79. data/test/dummy/app/javascript/packs/application.js +0 -0
  80. data/test/dummy/app/jobs/application_job.rb +0 -2
  81. data/test/dummy/app/mailboxes/application_mailbox.rb +0 -2
  82. data/test/dummy/app/mailboxes/messages_mailbox.rb +0 -4
  83. data/test/dummy/app/mailers/application_mailer.rb +0 -4
  84. data/test/dummy/app/models/application_record.rb +0 -3
  85. data/test/dummy/app/models/concerns/.keep +0 -0
  86. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  87. data/test/dummy/app/views/layouts/mailer.html.erb +0 -13
  88. data/test/dummy/app/views/layouts/mailer.text.erb +0 -1
  89. data/test/dummy/bin/bundle +0 -3
  90. data/test/dummy/bin/rails +0 -4
  91. data/test/dummy/bin/rake +0 -4
  92. data/test/dummy/bin/setup +0 -36
  93. data/test/dummy/bin/update +0 -31
  94. data/test/dummy/bin/yarn +0 -11
  95. data/test/dummy/config.ru +0 -5
  96. data/test/dummy/config/application.rb +0 -19
  97. data/test/dummy/config/boot.rb +0 -5
  98. data/test/dummy/config/cable.yml +0 -10
  99. data/test/dummy/config/database.yml +0 -25
  100. data/test/dummy/config/environment.rb +0 -5
  101. data/test/dummy/config/environments/development.rb +0 -63
  102. data/test/dummy/config/environments/production.rb +0 -96
  103. data/test/dummy/config/environments/test.rb +0 -46
  104. data/test/dummy/config/initializers/application_controller_renderer.rb +0 -8
  105. data/test/dummy/config/initializers/assets.rb +0 -14
  106. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  107. data/test/dummy/config/initializers/content_security_policy.rb +0 -22
  108. data/test/dummy/config/initializers/cookies_serializer.rb +0 -5
  109. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  110. data/test/dummy/config/initializers/inflections.rb +0 -16
  111. data/test/dummy/config/initializers/mime_types.rb +0 -4
  112. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  113. data/test/dummy/config/locales/en.yml +0 -33
  114. data/test/dummy/config/puma.rb +0 -34
  115. data/test/dummy/config/routes.rb +0 -4
  116. data/test/dummy/config/spring.rb +0 -6
  117. data/test/dummy/config/storage.yml +0 -35
  118. data/test/dummy/config/webpack/development.js +0 -3
  119. data/test/dummy/config/webpack/environment.js +0 -3
  120. data/test/dummy/config/webpack/production.js +0 -3
  121. data/test/dummy/config/webpack/test.js +0 -3
  122. data/test/dummy/config/webpacker.yml +0 -65
  123. data/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb +0 -11
  124. data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +0 -26
  125. data/test/dummy/db/schema.rb +0 -43
  126. data/test/dummy/lib/assets/.keep +0 -0
  127. data/test/dummy/log/.keep +0 -0
  128. data/test/dummy/package.json +0 -11
  129. data/test/dummy/public/404.html +0 -67
  130. data/test/dummy/public/422.html +0 -67
  131. data/test/dummy/public/500.html +0 -66
  132. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  133. data/test/dummy/public/apple-touch-icon.png +0 -0
  134. data/test/dummy/public/favicon.ico +0 -0
  135. data/test/dummy/storage/.keep +0 -0
  136. data/test/dummy/yarn.lock +0 -6071
  137. data/test/fixtures/files/welcome.eml +0 -631
  138. data/test/jobs/incineration_job_test.rb +0 -17
  139. data/test/test_helper.rb +0 -54
  140. data/test/unit/inbound_email/incineration_test.rb +0 -45
  141. data/test/unit/inbound_email/message_id_test.rb +0 -13
  142. data/test/unit/inbound_email_test.rb +0 -13
  143. data/test/unit/mail_ext/address_equality_test.rb +0 -9
  144. data/test/unit/mail_ext/address_wrapping_test.rb +0 -11
  145. data/test/unit/mail_ext/recipients_test.rb +0 -33
  146. data/test/unit/mailbox/bouncing_test.rb +0 -29
  147. data/test/unit/mailbox/callbacks_test.rb +0 -75
  148. data/test/unit/mailbox/routing_test.rb +0 -30
  149. data/test/unit/mailbox/state_test.rb +0 -49
  150. data/test/unit/postfix_relayer_test.rb +0 -90
  151. data/test/unit/router_test.rb +0 -137
@@ -1,7 +1,9 @@
1
- # A newly received `InboundEmail` will not be routed synchronously as part of ingress controller's receival.
2
- # Instead, the routing will be done asynchronously, using a `RoutingJob`, to ensure maximum parallel capacity.
1
+ # frozen_string_literal: true
2
+
3
+ # A newly received +InboundEmail+ will not be routed synchronously as part of ingress controller's receival.
4
+ # Instead, the routing will be done asynchronously, using a +RoutingJob+, to ensure maximum parallel capacity.
3
5
  #
4
- # By default, all newly created `InboundEmail` records that have the status of `pending`, which is the default,
6
+ # By default, all newly created +InboundEmail+ records that have the status of +pending+, which is the default,
5
7
  # will be scheduled for automatic, deferred routing.
6
8
  module ActionMailbox::InboundEmail::Routable
7
9
  extend ActiveSupport::Concern
@@ -10,12 +12,12 @@ module ActionMailbox::InboundEmail::Routable
10
12
  after_create_commit :route_later, if: :pending?
11
13
  end
12
14
 
13
- # Enqueue a `RoutingJob` for this `InboundEmail`.
15
+ # Enqueue a +RoutingJob+ for this +InboundEmail+.
14
16
  def route_later
15
17
  ActionMailbox::RoutingJob.perform_later self
16
18
  end
17
19
 
18
- # Route this `InboundEmail` using the routing rules declared on the `ApplicationMailbox`.
20
+ # Route this +InboundEmail+ using the routing rules declared on the +ApplicationMailbox+.
19
21
  def route
20
22
  ApplicationMailbox.route self
21
23
  end
@@ -38,5 +38,10 @@
38
38
  <%= form.text_area :body, size: "40x20" %>
39
39
  </div>
40
40
 
41
+ <div>
42
+ <%= form.label :attachments, "Attachments" %><br>
43
+ <%= form.file_field :attachments, multiple: true %>
44
+ </div>
45
+
41
46
  <%= form.submit "Deliver inbound email" %>
42
47
  <% end %>
@@ -4,7 +4,8 @@ Rails.application.routes.draw do
4
4
  scope "/rails/action_mailbox", module: "action_mailbox/ingresses" do
5
5
  post "/amazon/inbound_emails" => "amazon/inbound_emails#create", as: :rails_amazon_inbound_emails
6
6
  post "/mandrill/inbound_emails" => "mandrill/inbound_emails#create", as: :rails_mandrill_inbound_emails
7
- post "/postfix/inbound_emails" => "postfix/inbound_emails#create", as: :rails_postfix_inbound_emails
7
+ post "/postmark/inbound_emails" => "postmark/inbound_emails#create", as: :rails_postmark_inbound_emails
8
+ post "/relay/inbound_emails" => "relay/inbound_emails#create", as: :rails_relay_inbound_emails
8
9
  post "/sendgrid/inbound_emails" => "sendgrid/inbound_emails#create", as: :rails_sendgrid_inbound_emails
9
10
 
10
11
  # Mailgun requires that a webhook's URL end in 'mime' for it to receive the raw contents of emails.
@@ -1,11 +1,17 @@
1
- class CreateActionMailboxTables < ActiveRecord::Migration[5.2]
1
+ class CreateActionMailboxTables < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  create_table :action_mailbox_inbound_emails do |t|
4
4
  t.integer :status, default: 0, null: false
5
- t.string :message_id
5
+ t.string :message_id, null: false
6
+ t.string :message_checksum, null: false
6
7
 
7
- t.datetime :created_at, precision: 6
8
- t.datetime :updated_at, precision: 6
8
+ if supports_datetime_with_precision?
9
+ t.timestamps precision: 6
10
+ else
11
+ t.timestamps
12
+ end
13
+
14
+ t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true
9
15
  end
10
16
  end
11
17
  end
@@ -1,4 +1,5 @@
1
- require "action_mailbox/engine"
1
+ # frozen_string_literal: true
2
+
2
3
  require "action_mailbox/mail_ext"
3
4
 
4
5
  module ActionMailbox
@@ -1,111 +1,118 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/rescuable"
2
4
 
3
5
  require "action_mailbox/callbacks"
4
6
  require "action_mailbox/routing"
5
7
 
6
- # The base class for all application mailboxes. Not intended to be inherited from directly. Inherit from
7
- # `ApplicationMailbox` instead, as that's where the app-specific routing is configured. This routing
8
- # is specified in the following ways:
9
- #
10
- # class ApplicationMailbox < ActionMailbox::Base
11
- # # Any of the recipients of the mail (whether to, cc, bcc) are matched against the regexp.
12
- # route /^replies@/i => :replies
13
- #
14
- # # Any of the recipients of the mail (whether to, cc, bcc) needs to be an exact match for the string.
15
- # route "help@example.com" => :help
16
- #
17
- # # Any callable (proc, lambda, etc) object is passed the inbound_email record and is a match if true.
18
- # route ->(inbound_email) { inbound_email.mail.to.size > 2 } => :multiple_recipients
19
- #
20
- # # Any object responding to #match? is called with the inbound_email record as an argument. Match if true.
21
- # route CustomAddress.new => :custom
22
- #
23
- # # Any inbound_email that has not been already matched will be sent to the BackstopMailbox.
24
- # route :all => :backstop
25
- # end
26
- #
27
- # Application mailboxes need to overwrite the `#process` method, which is invoked by the framework after
28
- # callbacks have been run. The callbacks available are: `before_processing`, `after_processing`, and
29
- # `around_processing`. The primary use case is ensure certain preconditions to processing are fulfilled
30
- # using `before_processing` callbacks.
31
- #
32
- # If a precondition fails to be met, you can halt the processing using the `#bounced!` method,
33
- # which will silently prevent any further processing, but not actually send out any bounce notice. You
34
- # can also pair this behavior with the invocation of an Action Mailer class responsible for sending out
35
- # an actual bounce email. This is done using the `#bounce_with` method, which takes the mail object returned
36
- # by an Action Mailer method, like so:
37
- #
38
- # class ForwardsMailbox < ApplicationMailbox
39
- # before_processing :ensure_sender_is_a_user
40
- #
41
- # private
42
- # def ensure_sender_is_a_user
43
- # unless User.exist?(email_address: mail.from)
44
- # bounce_with UserRequiredMailer.missing(inbound_email)
45
- # end
46
- # end
47
- # end
48
- #
49
- # During the processing of the inbound email, the status will be tracked. Before processing begins,
50
- # the email will normally have the `pending` status. Once processing begins, just before callbacks
51
- # and the `#process` method is called, the status is changed to `processing`. If processing is allowed to
52
- # complete, the status is changed to `delivered`. If a bounce is triggered, then `bounced`. If an unhandled
53
- # exception is bubbled up, then `failed`.
54
- #
55
- # Exceptions can be handled at the class level using the familiar `Rescuable` approach:
56
- #
57
- # class ForwardsMailbox < ApplicationMailbox
58
- # rescue_from(ApplicationSpecificVerificationError) { bounced! }
59
- # end
60
- class ActionMailbox::Base
61
- include ActiveSupport::Rescuable
62
- include ActionMailbox::Callbacks, ActionMailbox::Routing
8
+ module ActionMailbox
9
+ # The base class for all application mailboxes. Not intended to be inherited from directly. Inherit from
10
+ # +ApplicationMailbox+ instead, as that's where the app-specific routing is configured. This routing
11
+ # is specified in the following ways:
12
+ #
13
+ # class ApplicationMailbox < ActionMailbox::Base
14
+ # # Any of the recipients of the mail (whether to, cc, bcc) are matched against the regexp.
15
+ # routing /^replies@/i => :replies
16
+ #
17
+ # # Any of the recipients of the mail (whether to, cc, bcc) needs to be an exact match for the string.
18
+ # routing "help@example.com" => :help
19
+ #
20
+ # # Any callable (proc, lambda, etc) object is passed the inbound_email record and is a match if true.
21
+ # routing ->(inbound_email) { inbound_email.mail.to.size > 2 } => :multiple_recipients
22
+ #
23
+ # # Any object responding to #match? is called with the inbound_email record as an argument. Match if true.
24
+ # routing CustomAddress.new => :custom
25
+ #
26
+ # # Any inbound_email that has not been already matched will be sent to the BackstopMailbox.
27
+ # routing :all => :backstop
28
+ # end
29
+ #
30
+ # Application mailboxes need to overwrite the +#process+ method, which is invoked by the framework after
31
+ # callbacks have been run. The callbacks available are: +before_processing+, +after_processing+, and
32
+ # +around_processing+. The primary use case is ensure certain preconditions to processing are fulfilled
33
+ # using +before_processing+ callbacks.
34
+ #
35
+ # If a precondition fails to be met, you can halt the processing using the +#bounced!+ method,
36
+ # which will silently prevent any further processing, but not actually send out any bounce notice. You
37
+ # can also pair this behavior with the invocation of an Action Mailer class responsible for sending out
38
+ # an actual bounce email. This is done using the +#bounce_with+ method, which takes the mail object returned
39
+ # by an Action Mailer method, like so:
40
+ #
41
+ # class ForwardsMailbox < ApplicationMailbox
42
+ # before_processing :ensure_sender_is_a_user
43
+ #
44
+ # private
45
+ # def ensure_sender_is_a_user
46
+ # unless User.exist?(email_address: mail.from)
47
+ # bounce_with UserRequiredMailer.missing(inbound_email)
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # During the processing of the inbound email, the status will be tracked. Before processing begins,
53
+ # the email will normally have the +pending+ status. Once processing begins, just before callbacks
54
+ # and the +#process+ method is called, the status is changed to +processing+. If processing is allowed to
55
+ # complete, the status is changed to +delivered+. If a bounce is triggered, then +bounced+. If an unhandled
56
+ # exception is bubbled up, then +failed+.
57
+ #
58
+ # Exceptions can be handled at the class level using the familiar +Rescuable+ approach:
59
+ #
60
+ # class ForwardsMailbox < ApplicationMailbox
61
+ # rescue_from(ApplicationSpecificVerificationError) { bounced! }
62
+ # end
63
+ class Base
64
+ include ActiveSupport::Rescuable
65
+ include ActionMailbox::Callbacks, ActionMailbox::Routing
63
66
 
64
- attr_reader :inbound_email
65
- delegate :mail, :delivered!, :bounced!, to: :inbound_email
67
+ attr_reader :inbound_email
68
+ delegate :mail, :delivered!, :bounced!, to: :inbound_email
66
69
 
67
- delegate :logger, to: ActionMailbox
70
+ delegate :logger, to: ActionMailbox
68
71
 
69
- def self.receive(inbound_email)
70
- new(inbound_email).perform_processing
71
- end
72
+ def self.receive(inbound_email)
73
+ new(inbound_email).perform_processing
74
+ end
72
75
 
73
- def initialize(inbound_email)
74
- @inbound_email = inbound_email
75
- end
76
+ def initialize(inbound_email)
77
+ @inbound_email = inbound_email
78
+ end
76
79
 
77
- def perform_processing
78
- track_status_of_inbound_email do
79
- run_callbacks :process do
80
- process
80
+ def perform_processing #:nodoc:
81
+ track_status_of_inbound_email do
82
+ run_callbacks :process do
83
+ process
84
+ end
81
85
  end
86
+ rescue => exception
87
+ # TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier
88
+ rescue_with_handler(exception) || raise
82
89
  end
83
- rescue => exception
84
- # TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier
85
- rescue_with_handler(exception) || raise
86
- end
87
-
88
- def process
89
- # Overwrite in subclasses
90
- end
91
90
 
92
- def finished_processing?
93
- inbound_email.delivered? || inbound_email.bounced?
94
- end
91
+ def process
92
+ # Overwrite in subclasses
93
+ end
95
94
 
95
+ def finished_processing? #:nodoc:
96
+ inbound_email.delivered? || inbound_email.bounced?
97
+ end
96
98
 
97
- def bounce_with(message)
98
- inbound_email.bounced!
99
- message.deliver_later
100
- end
101
99
 
102
- private
103
- def track_status_of_inbound_email
104
- inbound_email.processing!
105
- yield
106
- inbound_email.delivered! unless inbound_email.bounced?
107
- rescue
108
- inbound_email.failed!
109
- raise
100
+ # Enqueues the given +message+ for delivery and changes the inbound email's status to +:bounced+.
101
+ def bounce_with(message)
102
+ inbound_email.bounced!
103
+ message.deliver_later
110
104
  end
105
+
106
+ private
107
+ def track_status_of_inbound_email
108
+ inbound_email.processing!
109
+ yield
110
+ inbound_email.delivered! unless inbound_email.bounced?
111
+ rescue
112
+ inbound_email.failed!
113
+ raise
114
+ end
115
+ end
111
116
  end
117
+
118
+ ActiveSupport.run_load_hooks :action_mailbox, ActionMailbox::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/callbacks"
2
4
 
3
5
  module ActionMailbox
@@ -15,7 +17,7 @@ module ActionMailbox
15
17
  define_callbacks :process, terminator: TERMINATOR, skip_after_callbacks_if_terminated: true
16
18
  end
17
19
 
18
- module ClassMethods
20
+ class_methods do
19
21
  def before_processing(*methods, &block)
20
22
  set_callback(:process, :before, *methods, &block)
21
23
  end
@@ -1,4 +1,12 @@
1
- require "rails/engine"
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "action_controller/railtie"
5
+ require "active_job/railtie"
6
+ require "active_record/railtie"
7
+ require "active_storage/engine"
8
+
9
+ require "action_mailbox"
2
10
 
3
11
  module ActionMailbox
4
12
  class Engine < Rails::Engine
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailbox
4
+ # Returns the currently-loaded version of Action Mailbox as a <tt>Gem::Version</tt>.
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "mail"
2
4
 
3
5
  # The hope is to upstream most of these basic additions to the Mail gem's Mail object. But until then, here they lay!
@@ -1,5 +1,9 @@
1
- class Mail::Address
2
- def ==(other_address)
3
- other_address.is_a?(Mail::Address) && to_s == other_address.to_s
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class Address
5
+ def ==(other_address)
6
+ other_address.is_a?(Mail::Address) && to_s == other_address.to_s
7
+ end
4
8
  end
5
9
  end
@@ -1,5 +1,9 @@
1
- class Mail::Address
2
- def self.wrap(address)
3
- address.is_a?(Mail::Address) ? address : Mail::Address.new(address)
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class Address
5
+ def self.wrap(address)
6
+ address.is_a?(Mail::Address) ? address : Mail::Address.new(address)
7
+ end
4
8
  end
5
9
  end
@@ -1,25 +1,29 @@
1
- class Mail::Message
2
- def from_address
3
- header[:from]&.address_list&.addresses&.first
4
- end
1
+ # frozen_string_literal: true
5
2
 
6
- def recipients_addresses
7
- to_addresses + cc_addresses + bcc_addresses + x_original_to_addresses
8
- end
3
+ module Mail
4
+ class Message
5
+ def from_address
6
+ header[:from]&.address_list&.addresses&.first
7
+ end
9
8
 
10
- def to_addresses
11
- Array(header[:to]&.address_list&.addresses)
12
- end
9
+ def recipients_addresses
10
+ to_addresses + cc_addresses + bcc_addresses + x_original_to_addresses
11
+ end
13
12
 
14
- def cc_addresses
15
- Array(header[:cc]&.address_list&.addresses)
16
- end
13
+ def to_addresses
14
+ Array(header[:to]&.address_list&.addresses)
15
+ end
17
16
 
18
- def bcc_addresses
19
- Array(header[:bcc]&.address_list&.addresses)
20
- end
17
+ def cc_addresses
18
+ Array(header[:cc]&.address_list&.addresses)
19
+ end
20
+
21
+ def bcc_addresses
22
+ Array(header[:bcc]&.address_list&.addresses)
23
+ end
21
24
 
22
- def x_original_to_addresses
23
- Array(header[:x_original_to]).collect { |header| Mail::Address.new header.to_s }
25
+ def x_original_to_addresses
26
+ Array(header[:x_original_to]).collect { |header| Mail::Address.new header.to_s }
27
+ end
24
28
  end
25
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mail
2
4
  def self.from_source(source)
3
5
  Mail.new Mail::Utilities.binary_unsafe_to_crlf(source.to_s)
@@ -1,5 +1,9 @@
1
- class Mail::Message
2
- def recipients
3
- Array(to) + Array(cc) + Array(bcc) + Array(header[:x_original_to]).map(&:to_s)
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class Message
5
+ def recipients
6
+ Array(to) + Array(cc) + Array(bcc) + Array(header[:x_original_to]).map(&:to_s)
7
+ end
4
8
  end
5
9
  end
@@ -5,19 +5,27 @@ require "net/http"
5
5
  require "uri"
6
6
 
7
7
  module ActionMailbox
8
- class PostfixRelayer
9
- class Result < Struct.new(:output)
8
+ class Relayer
9
+ class Result < Struct.new(:status_code, :message)
10
10
  def success?
11
11
  !failure?
12
12
  end
13
13
 
14
14
  def failure?
15
- output.match?(/\A[45]\.\d{1,3}\.\d{1,3}(\s|\z)/)
15
+ transient_failure? || permanent_failure?
16
+ end
17
+
18
+ def transient_failure?
19
+ status_code.start_with?("4.")
20
+ end
21
+
22
+ def permanent_failure?
23
+ status_code.start_with?("5.")
16
24
  end
17
25
  end
18
26
 
19
27
  CONTENT_TYPE = "message/rfc822"
20
- USER_AGENT = "Action Mailbox Postfix relayer v#{ActionMailbox::VERSION}"
28
+ USER_AGENT = "Action Mailbox relayer v#{ActionMailbox.version}"
21
29
 
22
30
  attr_reader :uri, :username, :password
23
31
 
@@ -28,18 +36,18 @@ module ActionMailbox
28
36
  def relay(source)
29
37
  case response = post(source)
30
38
  when Net::HTTPSuccess
31
- Result.new "2.0.0 Successfully relayed message to Postfix ingress"
39
+ Result.new "2.0.0", "Successfully relayed message to ingress"
32
40
  when Net::HTTPUnauthorized
33
- Result.new "4.7.0 Invalid credentials for Postfix ingress"
41
+ Result.new "4.7.0", "Invalid credentials for ingress"
34
42
  else
35
- Result.new "4.0.0 HTTP #{response.code}"
43
+ Result.new "4.0.0", "HTTP #{response.code}"
36
44
  end
37
45
  rescue IOError, SocketError, SystemCallError => error
38
- Result.new "4.4.2 Network error relaying to Postfix ingress: #{error.message}"
46
+ Result.new "4.4.2", "Network error relaying to ingress: #{error.message}"
39
47
  rescue Timeout::Error
40
- Result.new "4.4.2 Timed out relaying to Postfix ingress"
48
+ Result.new "4.4.2", "Timed out relaying to ingress"
41
49
  rescue => error
42
- Result.new "4.0.0 Error relaying to Postfix ingress: #{error.message}"
50
+ Result.new "4.0.0", "Error relaying to ingress: #{error.message}"
43
51
  end
44
52
 
45
53
  private