diff --git a/app/controllers/admin/email_controller.rb b/app/controllers/admin/email_controller.rb index 55b134965..832efd85e 100644 --- a/app/controllers/admin/email_controller.rb +++ b/app/controllers/admin/email_controller.rb @@ -52,7 +52,7 @@ class Admin::EmailController < Admin::AdminController def handle_mail params.require(:email) - Email::Receiver.new(params[:email]).process! + Email::Processor.process!(params[:email]) render text: "email was processed" end diff --git a/app/jobs/scheduled/poll_mailbox.rb b/app/jobs/scheduled/poll_mailbox.rb index 89de500cb..837bef850 100644 --- a/app/jobs/scheduled/poll_mailbox.rb +++ b/app/jobs/scheduled/poll_mailbox.rb @@ -1,5 +1,6 @@ require 'net/pop' require_dependency 'email/receiver' +require_dependency 'email/processor' require_dependency 'email/sender' require_dependency 'email/message_builder' @@ -21,88 +22,7 @@ module Jobs end def process_popmail(popmail) - begin - mail_string = popmail.pop - receiver = Email::Receiver.new(mail_string) - receiver.process! - rescue Email::Receiver::BouncedEmailError => e - log_email_process_failure(mail_string, e) - - set_incoming_email_rejection_message( - receiver.incoming_email, - I18n.t("emails.incoming.errors.bounced_email_error") - ) - rescue Email::Receiver::AutoGeneratedEmailReplyError => e - log_email_process_failure(mail_string, e) - - set_incoming_email_rejection_message( - receiver.incoming_email, - I18n.t("emails.incoming.errors.auto_generated_email_reply") - ) - rescue => e - rejection_message = handle_failure(mail_string, e) - if rejection_message.present? && receiver && (incoming_email = receiver.incoming_email) - set_incoming_email_rejection_message(incoming_email, rejection_message.body.to_s) - end - end - end - - def handle_failure(mail_string, e) - log_email_process_failure(mail_string, e) - - message_template = case e - when Email::Receiver::EmptyEmailError then :email_reject_empty - when Email::Receiver::NoBodyDetectedError then :email_reject_empty - when Email::Receiver::UserNotFoundError then :email_reject_user_not_found - when Email::Receiver::ScreenedEmailError then :email_reject_screened_email - when Email::Receiver::AutoGeneratedEmailError then :email_reject_auto_generated - when Email::Receiver::InactiveUserError then :email_reject_inactive_user - when Email::Receiver::BlockedUserError then :email_reject_blocked_user - when Email::Receiver::BadDestinationAddress then :email_reject_bad_destination_address - when Email::Receiver::StrangersNotAllowedError then :email_reject_strangers_not_allowed - when Email::Receiver::InsufficientTrustLevelError then :email_reject_insufficient_trust_level - when Email::Receiver::ReplyUserNotMatchingError then :email_reject_reply_user_not_matching - when Email::Receiver::TopicNotFoundError then :email_reject_topic_not_found - when Email::Receiver::TopicClosedError then :email_reject_topic_closed - when Email::Receiver::InvalidPost then :email_reject_invalid_post - 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 - when RateLimiter::LimitExceeded then :email_reject_rate_limit_specified - end - - template_args = {} - client_message = nil - - # there might be more information available in the exception - if message_template == :email_reject_invalid_post && e.message.size > 6 - message_template = :email_reject_invalid_post_specified - template_args[:post_error] = e.message - end - - if message_template == :email_reject_rate_limit_specified - template_args[:rate_limit_description] = e.description - end - - if message_template == :email_reject_auto_generated - template_args[:mark_as_reply_to_auto_generated] = true - end - - if message_template - # inform the user about the rejection - message = Mail::Message.new(mail_string) - template_args[:former_title] = message.subject - template_args[:destination] = message.to - template_args[:site_name] = SiteSetting.title - - client_message = RejectionMailer.send_rejection(message_template, message.from, template_args) - Email::Sender.new(client_message, message_template).send - else - mark_as_errored! - Discourse.handle_job_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string)) - end - - client_message + Email::Processor.process!(popmail.pop) end POLL_MAILBOX_TIMEOUT_ERROR_KEY = "poll_mailbox_timeout_error_key".freeze @@ -148,18 +68,6 @@ module Jobs $redis.zadd(POLL_MAILBOX_ERRORS_KEY, now, now.to_s) end - private - - def set_incoming_email_rejection_message(incoming_email, message) - incoming_email.update_attributes!(rejection_message: message) - end - - def log_email_process_failure(mail_string, exception) - if SiteSetting.log_mail_processing_failures - Rails.logger.warn("Email can not be processed: #{exception}\n\n#{mail_string}") - end - end - def add_admin_dashboard_problem_message(i18n_key) AdminDashboardData.add_problem_message( i18n_key, diff --git a/lib/email/processor.rb b/lib/email/processor.rb new file mode 100644 index 000000000..85dace97f --- /dev/null +++ b/lib/email/processor.rb @@ -0,0 +1,102 @@ +module Email + + class Processor + + def initialize(mail) + @mail = mail + end + + def self.process!(mail) + Email::Processor.new(mail).process! + end + + def process! + begin + receiver = Email::Receiver.new(@mail) + receiver.process! + rescue Email::Receiver::BouncedEmailError => e + log_email_process_failure(@mail, e) + set_incoming_email_rejection_message(receiver.incoming_email, I18n.t("emails.incoming.errors.bounced_email_error")) + rescue Email::Receiver::AutoGeneratedEmailReplyError => e + log_email_process_failure(@mail, e) + set_incoming_email_rejection_message(receiver.incoming_email, I18n.t("emails.incoming.errors.auto_generated_email_reply")) + rescue => e + log_email_process_failure(@mail, e) + + rejection_message = handle_failure(@mail, e) + if rejection_message.present? && receiver && (incoming_email = receiver.incoming_email) + set_incoming_email_rejection_message(incoming_email, rejection_message.body.to_s) + end + end + end + + private + + def handle_failure(mail_string, e) + message_template = case e + when Email::Receiver::EmptyEmailError then :email_reject_empty + when Email::Receiver::NoBodyDetectedError then :email_reject_empty + when Email::Receiver::UserNotFoundError then :email_reject_user_not_found + when Email::Receiver::ScreenedEmailError then :email_reject_screened_email + when Email::Receiver::AutoGeneratedEmailError then :email_reject_auto_generated + when Email::Receiver::InactiveUserError then :email_reject_inactive_user + when Email::Receiver::BlockedUserError then :email_reject_blocked_user + when Email::Receiver::BadDestinationAddress then :email_reject_bad_destination_address + when Email::Receiver::StrangersNotAllowedError then :email_reject_strangers_not_allowed + when Email::Receiver::InsufficientTrustLevelError then :email_reject_insufficient_trust_level + when Email::Receiver::ReplyUserNotMatchingError then :email_reject_reply_user_not_matching + when Email::Receiver::TopicNotFoundError then :email_reject_topic_not_found + when Email::Receiver::TopicClosedError then :email_reject_topic_closed + when Email::Receiver::InvalidPost then :email_reject_invalid_post + 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 + when RateLimiter::LimitExceeded then :email_reject_rate_limit_specified + end + + template_args = {} + client_message = nil + + # there might be more information available in the exception + if message_template == :email_reject_invalid_post && e.message.size > 6 + message_template = :email_reject_invalid_post_specified + template_args[:post_error] = e.message + end + + if message_template == :email_reject_rate_limit_specified + template_args[:rate_limit_description] = e.description + end + + if message_template == :email_reject_auto_generated + template_args[:mark_as_reply_to_auto_generated] = true + end + + if message_template + # inform the user about the rejection + message = Mail::Message.new(mail_string) + template_args[:former_title] = message.subject + template_args[:destination] = message.to + template_args[:site_name] = SiteSetting.title + + client_message = RejectionMailer.send_rejection(message_template, message.from, template_args) + Email::Sender.new(client_message, message_template).send + else + Discourse.handle_job_exception(e, error_context(@args, "Unrecognized error type when processing incoming email", mail: mail_string)) + end + + client_message + end + + def set_incoming_email_rejection_message(incoming_email, message) + incoming_email.update_attributes!(rejection_message: message) + end + + def log_email_process_failure(mail_string, exception) + if SiteSetting.log_mail_processing_failures + Rails.logger.warn("Email can not be processed: #{exception}\n\n#{mail_string}") + end + end + + end + +end