From 93d1cc6294085008e025bb3032b231a6a81c6480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Thu, 10 Dec 2015 23:49:16 +0100 Subject: [PATCH] add support for incoming emails in CC/BCC fields --- app/jobs/scheduled/poll_mailbox.rb | 11 +++---- app/models/email_log.rb | 14 +++++--- lib/email/receiver.rb | 16 +++++---- spec/components/email/receiver_spec.rb | 42 ++++++++++++++++++++---- spec/fixtures/emails/valid_incoming.eml | Bin 1228 -> 1237 bytes 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/app/jobs/scheduled/poll_mailbox.rb b/app/jobs/scheduled/poll_mailbox.rb index 68a1f986322..4dcf3f2882c 100644 --- a/app/jobs/scheduled/poll_mailbox.rb +++ b/app/jobs/scheduled/poll_mailbox.rb @@ -14,9 +14,7 @@ module Jobs def execute(args) @args = args - if SiteSetting.pop3_polling_enabled? - poll_pop3 - end + poll_pop3 if SiteSetting.pop3_polling_enabled? end def handle_mail(mail) @@ -31,7 +29,6 @@ module Jobs end def handle_failure(mail_string, e) - Rails.logger.warn("Email can not be processed: #{e}\n\n#{mail_string}") if SiteSetting.log_mail_processing_failures template_args = {} @@ -90,14 +87,14 @@ module Jobs connection.start(SiteSetting.pop3_polling_username, SiteSetting.pop3_polling_password) do |pop| unless pop.mails.empty? - pop.each do |mail| - handle_mail(mail) - end + pop.each { |mail| handle_mail(mail) } end pop.finish end rescue Net::POPAuthenticationError => e Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming email")) + rescue Net::POPError => e + Discourse.handle_job_exception(e, error_context(@args, "Generic POP error")) end end diff --git a/app/models/email_log.rb b/app/models/email_log.rb index bb324e1a73a..d806f3ef968 100644 --- a/app/models/email_log.rb +++ b/app/models/email_log.rb @@ -10,19 +10,25 @@ class EmailLog < ActiveRecord::Base after_create do # Update last_emailed_at if the user_id is present and email was sent - User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present? and !skipped + User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present? && !skipped end def self.count_per_day(start_date, end_date) - where('created_at >= ? and created_at < ? AND skipped = false', start_date, end_date).group('date(created_at)').order('date(created_at)').count + sent.where("created_at BETWEEN ? AND ?", start_date, end_date) + .group("DATE(created_at)") + .order("DATE(created_at)") + .count end def self.for(reply_key) - EmailLog.find_by(reply_key: reply_key) + self.find_by(reply_key: reply_key) end def self.last_sent_email_address - where(email_type: 'signup').order('created_at DESC').first.try(:to_address) + self.where(email_type: "signup") + .order(created_at: :desc) + .first + .try(:to_address) end end diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 07f89304e3b..3753cadd6c7 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -34,7 +34,8 @@ module Email body = parse_body(message) dest_info = { type: :invalid, obj: nil } - message.to.each do |to_address| + # 'smtp_envelope_to' is a combination of: to, cc and bcc fields + message.smtp_envelope_to.each do |to_address| dest_info = check_address(to_address) break if dest_info[:type] != :invalid end @@ -51,7 +52,6 @@ module Email case dest_info[:type] when :group - raise BadDestinationAddress unless SiteSetting.email_in group = dest_info[:obj] if @user.blank? @@ -68,7 +68,6 @@ module Email create_new_topic(archetype: Archetype.private_message, target_group_names: [group.name]) when :category - raise BadDestinationAddress unless SiteSetting.email_in category = dest_info[:obj] if @user.blank? && category.email_in_allow_strangers @@ -108,11 +107,14 @@ module Email end def check_address(address) - group = Group.find_by_email(address) - return { type: :group, obj: group } if group + # only check groups/categories when 'email_in' is enabled + if SiteSetting.email_in + group = Group.find_by_email(address) + return { type: :group, obj: group } if group - category = Category.find_by_email(address) - return { type: :category, obj: category } if category + category = Category.find_by_email(address) + return { type: :category, obj: category } if category + end regex = Regexp.escape(SiteSetting.reply_by_email_address) regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.*)") diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index b4af327b6a4..da5f792e752 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -474,16 +474,17 @@ This is a link http://example.com" end - def fill_email(mail, from, to, body = nil, subject = nil) + def fill_email(mail, from, to, body = nil, subject = nil, cc = nil) result = mail.gsub("FROM", from).gsub("TO", to) result.gsub!(/Hey.*/m, body) if body result.sub!(/We .*/, subject) if subject + result.sub!("CC", cc.presence || "") result end def process_email(opts) incoming_email = fixture_file("emails/valid_incoming.eml") - email = fill_email(incoming_email, opts[:from], opts[:to], opts[:body], opts[:subject]) + email = fill_email(incoming_email, opts[:from], opts[:to], opts[:body], opts[:subject], opts[:cc]) Email::Receiver.new(email).process end @@ -532,15 +533,15 @@ greatest show ever created. Everyone should watch it. end describe "processes an email to a category" do + let(:to) { "some@email.com" } + before do SiteSetting.email_in = true + SiteSetting.email_in_min_trust = TrustLevel[4].to_s end it "correctly can target categories" do - to = "some@email.com" - Fabricate(:category, email_in_allow_strangers: false, email_in: to) - SiteSetting.email_in_min_trust = TrustLevel[4].to_s # no email in for user expect{ @@ -614,7 +615,7 @@ greatest show ever created. Everyone should watch it. }.to raise_error(Email::Receiver::UserNotFoundError) end - it "creates a topic for allowed category" do + it "creates a topic for matching category" do Fabricate(:category, email_in_allow_strangers: true, email_in: email_in) process_email(from: user_email, to: email_in, body: body) @@ -636,7 +637,7 @@ greatest show ever created. Everyone should watch it. SiteSetting.allow_staged_accounts = true end - it "creates a message for allowed group" do + it "creates a message for matching group" do Fabricate(:group, incoming_email: incoming_email) process_email(from: user_email, to: incoming_email, body: body) @@ -652,4 +653,31 @@ greatest show ever created. Everyone should watch it. end + describe "supports incoming mail in CC fields" do + + let(:incoming_email) { "foo@bar.com" } + let(:user_email) { "#{SecureRandom.hex(32)}@foobar.com" } + let(:body) { "This is a message to\n\na group via CC ;)" } + + before do + SiteSetting.email_in = true + SiteSetting.allow_staged_accounts = true + end + + it "creates a message for matching group" do + Fabricate(:group, incoming_email: incoming_email) + process_email(from: user_email, to: "some@email.com", body: body, cc: incoming_email) + + staged_account = User.find_by_email(user_email) + expect(staged_account).to be + expect(staged_account.staged).to be(true) + + post = staged_account.posts.order(id: :desc).first + expect(post).to be + expect(post.raw).to eq(body) + expect(post.topic.private_message?).to eq(true) + end + + end + end diff --git a/spec/fixtures/emails/valid_incoming.eml b/spec/fixtures/emails/valid_incoming.eml index 5e8db53319ed6c07fa2b07392cf64f066c2f9449..cad475d662e4ad302975edb83137612bd667f75a 100644 GIT binary patch delta 20 bcmX@Zd6jd+AtrX`WGe+5XXnian0S}~O~wXv delta 12 Tcmcc0d4_YtA*RiznYfq$Bvk~o