diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index fd2357a0ed3..1ea6b56ab1b 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -88,6 +88,7 @@ en: maximum_staged_user_per_email_reached: "Reached maximum number of staged users created per email." no_subject: "(no subject)" no_body: "(no body)" + missing_attachment: '(Attachment %{filename} is missing)' errors: empty_email_error: "Happens when the raw mail we received was blank." no_message_id_error: "Happens when the mail has no 'Message-Id' header." @@ -2609,6 +2610,17 @@ en: There was an unrecognized error while processing your email and it wasn't posted. You should try again, or [contact a staff member](%{base_url}/about). + email_reject_attachment: + title: "Email Attachment Rejected" + subject_template: "[%{email_prefix}] Email issue -- Attachment Rejected" + text_body_template: | + Unfortunately some attachments in your email message to %{destination} (titled %{former_title}) were rejected. + + Details: + %{rejected_errors} + + If you believe this is an error, [contact a staff member](%{base_url}/about). + email_error_notification: title: "Email Error Notification" subject_template: "[%{email_prefix}] Email issue -- POP authentication error" diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 4cc9ec520a6..8e939d36ea6 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -878,12 +878,13 @@ module Email def create_post_with_attachments(options = {}) # deal with attachments - options[:raw] = add_attachments(options[:raw], options[:user].id, options) + options[:raw] = add_attachments(options[:raw], options[:user], options) create_post(options) end - def add_attachments(raw, user_id, options = {}) + def add_attachments(raw, user, options = {}) + rejected_attachments = [] attachments.each do |attachment| tmp = Tempfile.new(["discourse-email-attachment", File.extname(attachment.filename)]) begin @@ -891,7 +892,7 @@ module Email File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded } # create the upload for the user opts = { for_group_message: options[:is_group_message] } - upload = UploadCreator.new(tmp, attachment.filename, opts).create_for(user_id) + upload = UploadCreator.new(tmp, attachment.filename, opts).create_for(user.id) if upload&.valid? # try to inline images if attachment.content_type&.start_with?("image/") @@ -905,15 +906,39 @@ module Email else raw << "\n\n#{attachment_markdown(upload)}\n\n" end + else + rejected_attachments << upload + raw << "\n\n#{I18n.t('emails.incoming.missing_attachment', filename: upload.original_filename)}\n\n" end ensure tmp&.close! end end + notify_about_rejected_attachment(rejected_attachments) if rejected_attachments.present? && !user.staged? raw end + def notify_about_rejected_attachment(attachments) + errors = [] + + attachments.each do |a| + error = a.errors.messages.values[0][0] + errors << "#{a.original_filename}: #{error}" + end + + message = Mail::Message.new(@mail) + template_args = { + former_title: message.subject, + destination: message.to, + site_name: SiteSetting.title, + rejected_errors: errors.join("\n") + } + + client_message = RejectionMailer.send_rejection(:email_reject_attachment, message.from, template_args) + Email::Sender.new(client_message, :email_reject_attachment).send + end + def attachment_markdown(upload) if FileHelper.is_supported_image?(upload.original_filename) "" diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index dc00a9a1886..fb45da0ccca 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -465,10 +465,24 @@ describe Email::Receiver do SiteSetting.authorized_extensions = "txt" expect { process(:attached_txt_file) }.to change { topic.posts.count } expect(topic.posts.last.raw).to match(/text\.txt/) + end - SiteSetting.authorized_extensions = "csv" - expect { process(:attached_txt_file_2) }.to change { topic.posts.count } - expect(topic.posts.last.raw).to_not match(/text\.txt/) + context "when attachment is rejected" do + it "sends out the warning email" do + expect { process(:attached_txt_file) }.to change { EmailLog.count }.by(1) + expect(EmailLog.last.email_type).to eq("email_reject_attachment") + end + + it "doesn't send out the warning email if sender is staged user" do + user.update_columns(staged: true) + expect { process(:attached_txt_file) }.not_to change { EmailLog.count } + end + + it "creates the post with attachment missing message" do + missing_attachment_regex = Regexp.escape(I18n.t('emails.incoming.missing_attachment', filename: "text.txt")) + expect { process(:attached_txt_file) }.to change { topic.posts.count } + expect(topic.posts.last.raw).to match(/#{missing_attachment_regex}/) + end end it "supports emails with just an attachment" do