add support for incoming emails in CC/BCC fields

This commit is contained in:
Régis Hanol 2015-12-10 23:49:16 +01:00
parent 6c2dee29a8
commit 93d1cc6294
5 changed files with 59 additions and 25 deletions

View File

@ -14,9 +14,7 @@ module Jobs
def execute(args) def execute(args)
@args = args @args = args
if SiteSetting.pop3_polling_enabled? poll_pop3 if SiteSetting.pop3_polling_enabled?
poll_pop3
end
end end
def handle_mail(mail) def handle_mail(mail)
@ -31,7 +29,6 @@ module Jobs
end end
def handle_failure(mail_string, e) 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 Rails.logger.warn("Email can not be processed: #{e}\n\n#{mail_string}") if SiteSetting.log_mail_processing_failures
template_args = {} template_args = {}
@ -90,14 +87,14 @@ module Jobs
connection.start(SiteSetting.pop3_polling_username, SiteSetting.pop3_polling_password) do |pop| connection.start(SiteSetting.pop3_polling_username, SiteSetting.pop3_polling_password) do |pop|
unless pop.mails.empty? unless pop.mails.empty?
pop.each do |mail| pop.each { |mail| handle_mail(mail) }
handle_mail(mail)
end
end end
pop.finish pop.finish
end end
rescue Net::POPAuthenticationError => e rescue Net::POPAuthenticationError => e
Discourse.handle_job_exception(e, error_context(@args, "Signing in to poll incoming email")) 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
end end

View File

@ -10,19 +10,25 @@ class EmailLog < ActiveRecord::Base
after_create do after_create do
# Update last_emailed_at if the user_id is present and email was sent # 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 end
def self.count_per_day(start_date, end_date) 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 end
def self.for(reply_key) def self.for(reply_key)
EmailLog.find_by(reply_key: reply_key) self.find_by(reply_key: reply_key)
end end
def self.last_sent_email_address 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
end end

View File

@ -34,7 +34,8 @@ module Email
body = parse_body(message) body = parse_body(message)
dest_info = { type: :invalid, obj: nil } 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) dest_info = check_address(to_address)
break if dest_info[:type] != :invalid break if dest_info[:type] != :invalid
end end
@ -51,7 +52,6 @@ module Email
case dest_info[:type] case dest_info[:type]
when :group when :group
raise BadDestinationAddress unless SiteSetting.email_in
group = dest_info[:obj] group = dest_info[:obj]
if @user.blank? if @user.blank?
@ -68,7 +68,6 @@ module Email
create_new_topic(archetype: Archetype.private_message, target_group_names: [group.name]) create_new_topic(archetype: Archetype.private_message, target_group_names: [group.name])
when :category when :category
raise BadDestinationAddress unless SiteSetting.email_in
category = dest_info[:obj] category = dest_info[:obj]
if @user.blank? && category.email_in_allow_strangers if @user.blank? && category.email_in_allow_strangers
@ -108,11 +107,14 @@ module Email
end end
def check_address(address) def check_address(address)
group = Group.find_by_email(address) # only check groups/categories when 'email_in' is enabled
return { type: :group, obj: group } if group if SiteSetting.email_in
group = Group.find_by_email(address)
return { type: :group, obj: group } if group
category = Category.find_by_email(address) category = Category.find_by_email(address)
return { type: :category, obj: category } if category return { type: :category, obj: category } if category
end
regex = Regexp.escape(SiteSetting.reply_by_email_address) regex = Regexp.escape(SiteSetting.reply_by_email_address)
regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.*)") regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.*)")

View File

@ -474,16 +474,17 @@ This is a link http://example.com"
end 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 = mail.gsub("FROM", from).gsub("TO", to)
result.gsub!(/Hey.*/m, body) if body result.gsub!(/Hey.*/m, body) if body
result.sub!(/We .*/, subject) if subject result.sub!(/We .*/, subject) if subject
result.sub!("CC", cc.presence || "")
result result
end end
def process_email(opts) def process_email(opts)
incoming_email = fixture_file("emails/valid_incoming.eml") 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 Email::Receiver.new(email).process
end end
@ -532,15 +533,15 @@ greatest show ever created. Everyone should watch it.
end end
describe "processes an email to a category" do describe "processes an email to a category" do
let(:to) { "some@email.com" }
before do before do
SiteSetting.email_in = true SiteSetting.email_in = true
SiteSetting.email_in_min_trust = TrustLevel[4].to_s
end end
it "correctly can target categories" do it "correctly can target categories" do
to = "some@email.com"
Fabricate(:category, email_in_allow_strangers: false, email_in: to) Fabricate(:category, email_in_allow_strangers: false, email_in: to)
SiteSetting.email_in_min_trust = TrustLevel[4].to_s
# no email in for user # no email in for user
expect{ expect{
@ -614,7 +615,7 @@ greatest show ever created. Everyone should watch it.
}.to raise_error(Email::Receiver::UserNotFoundError) }.to raise_error(Email::Receiver::UserNotFoundError)
end 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) Fabricate(:category, email_in_allow_strangers: true, email_in: email_in)
process_email(from: user_email, to: email_in, body: body) 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 SiteSetting.allow_staged_accounts = true
end end
it "creates a message for allowed group" do it "creates a message for matching group" do
Fabricate(:group, incoming_email: incoming_email) Fabricate(:group, incoming_email: incoming_email)
process_email(from: user_email, to: incoming_email, body: body) process_email(from: user_email, to: incoming_email, body: body)
@ -652,4 +653,31 @@ greatest show ever created. Everyone should watch it.
end 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 end

View File

@ -6,6 +6,7 @@ Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
Date: Thu, 13 Jun 2013 17:03:48 -0400 Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <FROM> From: Jake the Dog <FROM>
To: <TO> To: <TO>
Cc: <CC>
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
Subject: We should have a post-by-email-feature. Subject: We should have a post-by-email-feature.
Mime-Version: 1.0 Mime-Version: 1.0