diff --git a/app/controllers/admin/email_templates_controller.rb b/app/controllers/admin/email_templates_controller.rb index d3d1e963289..2c3e44610db 100644 --- a/app/controllers/admin/email_templates_controller.rb +++ b/app/controllers/admin/email_templates_controller.rb @@ -15,6 +15,7 @@ class Admin::EmailTemplatesController < Admin::AdminController "system_messages.email_reject_reply_key", "system_messages.email_reject_topic_closed", "system_messages.email_reject_topic_not_found", "system_messages.email_reject_screened_email", + "system_messages.email_reject_unrecognized_error", "system_messages.pending_users_reminder", "system_messages.post_hidden", "system_messages.restore_failed", "system_messages.restore_succeeded", "system_messages.spam_post_blocked", "system_messages.too_many_spam_flags", diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 2fc2c220cc8..65fd41b488f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2269,6 +2269,14 @@ en: Your email was marked as "auto generated", which means it was automatically created by a computer instead of being typed by a human; we can't accept those kinds of emails. If you believe this is an error, contact a staff member. + email_reject_unrecognized_error: + title: "Email Reject Unrecognized Error" + subject_template: "[%{email_prefix}] Email issue -- Unrecognized Error" + text_body_template: | + We're sorry, but your email message to %{destination} (titled %{former_title}) didn't work. + + There was an unrecognized error while processing your email and it wasn't posted. You should try again, or contact a staff member. + email_error_notification: title: "Email Error Notification" subject_template: "[%{email_prefix}] Email issue -- POP authentication error" diff --git a/lib/email/processor.rb b/lib/email/processor.rb index 71c9b846f70..a477f58e490 100644 --- a/lib/email/processor.rb +++ b/lib/email/processor.rb @@ -52,6 +52,7 @@ module Email when ActiveRecord::Rollback then :email_reject_invalid_post when Email::Receiver::InvalidPostAction then :email_reject_invalid_post_action when Discourse::InvalidAccess then :email_reject_invalid_access + else :email_reject_unrecognized_error end template_args = {} @@ -63,6 +64,14 @@ module Email template_args[:post_error] = e.message end + if message_template == :email_reject_unrecognized_error + msg = "Unrecognized error type (#{e.class}: #{e.message}) when processing incoming email" + msg += "\n\nBacktrace:\n#{e.backtrace.map { |l| " #{l}" }.join("\n")}" + msg += "\n\nMail:\n#{mail_string}" + + Rails.logger.error(msg) + end + if message_template # inform the user about the rejection message = Mail::Message.new(mail_string) @@ -76,18 +85,14 @@ module Email if can_send_rejection_email?(message.from, message_template) Email::Sender.new(client_message, message_template).send end - else - msg = "Unrecognized error type (#{e.class}: #{e.message}) when processing incoming email" - msg += "\n\nBacktrace:\n#{e.backtrace.map { |l| " #{l}" }.join("\n")}" - msg += "\n\nMail:\n#{mail_string}" - - Rails.logger.error(msg) end client_message end def can_send_rejection_email?(email, type) + return true if type == :email_reject_unrecognized_error + key = "rejection_email:#{email}:#{type}:#{Date.today}" if $redis.setnx(key, "1") diff --git a/spec/components/email/processor_spec.rb b/spec/components/email/processor_spec.rb index c30b09ad4ee..b99ae784108 100644 --- a/spec/components/email/processor_spec.rb +++ b/spec/components/email/processor_spec.rb @@ -3,9 +3,11 @@ require "email/processor" describe Email::Processor do + let(:from) { "foo@bar.com" } + describe "rate limits" do - let(:mail) { "From: foo@bar.com\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" } + let(:mail) { "From: #{from}\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" } let(:limit_exceeded) { RateLimiter::LimitExceeded.new(10) } before do @@ -24,4 +26,64 @@ describe Email::Processor do end + context "known error" do + + let(:mail) { "From: #{from}\nTo: bar@foo.com" } + let(:mail2) { "From: #{from}\nTo: foo@foo.com" } + let(:mail3) { "From: #{from}\nTo: foobar@foo.com" } + + it "only sends one rejection email per day" do + key = "rejection_email:#{[from]}:email_reject_empty:#{Date.today}" + $redis.expire(key, 0) + + expect { + Email::Processor.process!(mail) + }.to change { EmailLog.count }.by(1) + + expect { + Email::Processor.process!(mail2) + }.to change { EmailLog.count }.by(0) + + Timecop.freeze(Date.today + 1) + key = "rejection_email:#{[from]}:email_reject_empty:#{Date.today}" + $redis.expire(key, 0) + + expect { + Email::Processor.process!(mail3) + }.to change { EmailLog.count }.by(1) + end + end + + context "unrecognized error" do + + let(:mail) { "From: #{from}\nTo: bar@foo.com\nSubject: FOO BAR\n\nFoo foo bar bar?" } + let(:mail2) { "From: #{from}\nTo: foo@foo.com\nSubject: BAR BAR\n\nBar bar bar bar?" } + + it "sends a rejection email on an unrecognized error" do + Email::Processor.any_instance.stubs(:can_send_rejection_email?).returns(true) + Email::Receiver.any_instance.stubs(:process_internal).raises("boom") + Rails.logger.expects(:error) + + Email::Processor.process!(mail) + expect(IncomingEmail.first.error).to eq("boom") + expect(IncomingEmail.first.rejection_message).to be_present + expect(EmailLog.first.email_type).to eq("email_reject_unrecognized_error") + end + + it "sends more than one rejection email per day" do + Email::Receiver.any_instance.stubs(:process_internal).raises("boom") + key = "rejection_email:#{[from]}:email_reject_unrecognized_error:#{Date.today}" + $redis.expire(key, 0) + + expect { + Email::Processor.process!(mail) + }.to change { EmailLog.count }.by(1) + + expect { + Email::Processor.process!(mail2) + }.to change { EmailLog.count }.by(1) + end + + end + end