FEATURE: Create hidden posts for received spam emails (#6010)
* Add possibility to add hidden posts with PostCreator * FEATURE: Create hidden posts for received spam emails Spamchecker usually have 3 results: HAM, SPAM and PROBABLY_SPAM SPAM gets usually directly rejected and needs no further handling. HAM is good message and usually gets passed unmodified. PROBABLY_SPAM gets an additional header to allow further processing. This change addes processing capabilities for such headers and marks new posts created as hidden when received via email.
This commit is contained in:
parent
6e3a2197f8
commit
28dd7fb562
|
@ -113,7 +113,8 @@ class Post < ActiveRecord::Base
|
|||
@hidden_reasons ||= Enum.new(flag_threshold_reached: 1,
|
||||
flag_threshold_reached_again: 2,
|
||||
new_user_spam_threshold_reached: 3,
|
||||
flagged_by_tl3_user: 4)
|
||||
flagged_by_tl3_user: 4,
|
||||
email_spam_header_found: 5)
|
||||
end
|
||||
|
||||
def self.types
|
||||
|
|
|
@ -1541,6 +1541,7 @@ en:
|
|||
log_mail_processing_failures: "Log all email processing failures to http://yoursitename.com/logs"
|
||||
email_in: "Allow users to post new topics via email (requires manual or pop3 polling). Configure the addresses in the \"Settings\" tab of each category."
|
||||
email_in_min_trust: "The minimum trust level a user needs to have to be allowed to post new topics via email."
|
||||
email_in_spam_header: "The email header to detect spam."
|
||||
email_prefix: "The [label] used in the subject of emails. It will default to 'title' if not set."
|
||||
email_site_title: "The title of the site used as the sender of emails from the site. Default to 'title' if not set. If your 'title' contains characters that are not allowed in email sender strings, use this setting."
|
||||
|
||||
|
|
|
@ -790,6 +790,13 @@ email:
|
|||
email_in_min_trust:
|
||||
default: 2
|
||||
enum: 'TrustLevelSetting'
|
||||
email_in_spam_header:
|
||||
type: enum
|
||||
default: 'none'
|
||||
choices:
|
||||
- none
|
||||
- X-Spam-Flag
|
||||
- X-Spam-Status
|
||||
email_prefix: ''
|
||||
email_site_title: ''
|
||||
disable_emails:
|
||||
|
|
|
@ -111,6 +111,8 @@ module Email
|
|||
raise FromReplyByAddressError if is_from_reply_by_email_address?
|
||||
raise ScreenedEmailError if ScreenedEmail.should_block?(@from_email)
|
||||
|
||||
hidden_reason_id = is_spam? ? Post.hidden_reasons[:email_spam_header_found] : nil
|
||||
|
||||
user = find_user(@from_email)
|
||||
|
||||
if user.present?
|
||||
|
@ -149,6 +151,7 @@ module Email
|
|||
create_reply(user: user,
|
||||
raw: body,
|
||||
elided: elided,
|
||||
hidden_reason_id: hidden_reason_id,
|
||||
post: post,
|
||||
topic: post.topic,
|
||||
skip_validations: user.staged?)
|
||||
|
@ -157,7 +160,7 @@ module Email
|
|||
|
||||
destinations.each do |destination|
|
||||
begin
|
||||
process_destination(destination, user, body, elided)
|
||||
process_destination(destination, user, body, elided, hidden_reason_id)
|
||||
rescue => e
|
||||
first_exception ||= e
|
||||
else
|
||||
|
@ -243,6 +246,17 @@ module Email
|
|||
@mail.header.to_s[/auto[\-_]?(response|submitted|replied|reply|generated|respond)|holidayreply|machinegenerated/i]
|
||||
end
|
||||
|
||||
def is_spam?
|
||||
case SiteSetting.email_in_spam_header
|
||||
when 'X-Spam-Flag'
|
||||
@mail[:x_spam_flag].to_s[/YES/i]
|
||||
when 'X-Spam-Status'
|
||||
@mail[:x_spam_status].to_s[/^Yes, /i]
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def select_body
|
||||
text = nil
|
||||
html = nil
|
||||
|
@ -541,7 +555,7 @@ module Email
|
|||
nil
|
||||
end
|
||||
|
||||
def process_destination(destination, user, body, elided)
|
||||
def process_destination(destination, user, body, elided, hidden_reason_id)
|
||||
return if SiteSetting.enable_forwarded_emails &&
|
||||
has_been_forwarded? &&
|
||||
process_forwarded_email(destination, user)
|
||||
|
@ -549,7 +563,7 @@ module Email
|
|||
case destination[:type]
|
||||
when :group
|
||||
group = destination[:obj]
|
||||
create_group_post(group, user, body, elided)
|
||||
create_group_post(group, user, body, elided, hidden_reason_id)
|
||||
|
||||
when :category
|
||||
category = destination[:obj]
|
||||
|
@ -560,6 +574,7 @@ module Email
|
|||
create_topic(user: user,
|
||||
raw: body,
|
||||
elided: elided,
|
||||
hidden_reason_id: hidden_reason_id,
|
||||
title: subject,
|
||||
category: category.id,
|
||||
skip_validations: user.staged?)
|
||||
|
@ -574,13 +589,14 @@ module Email
|
|||
create_reply(user: user,
|
||||
raw: body,
|
||||
elided: elided,
|
||||
hidden_reason_id: hidden_reason_id,
|
||||
post: email_log.post,
|
||||
topic: email_log.post.topic,
|
||||
skip_validations: user.staged?)
|
||||
end
|
||||
end
|
||||
|
||||
def create_group_post(group, user, body, elided)
|
||||
def create_group_post(group, user, body, elided, hidden_reason_id)
|
||||
message_ids = Email::Receiver.extract_reply_message_ids(@mail, max_message_id_count: 5)
|
||||
post_ids = []
|
||||
|
||||
|
@ -598,6 +614,7 @@ module Email
|
|||
create_reply(user: user,
|
||||
raw: body,
|
||||
elided: elided,
|
||||
hidden_reason_id: hidden_reason_id,
|
||||
post: post,
|
||||
topic: post.topic,
|
||||
skip_validations: true)
|
||||
|
@ -605,6 +622,7 @@ module Email
|
|||
create_topic(user: user,
|
||||
raw: body,
|
||||
elided: elided,
|
||||
hidden_reason_id: hidden_reason_id,
|
||||
title: subject,
|
||||
archetype: Archetype.private_message,
|
||||
target_group_names: [group.name],
|
||||
|
|
|
@ -36,6 +36,7 @@ class PostCreator
|
|||
# wrap `PostCreator` in a transaction, as the sidekiq jobs could
|
||||
# dequeue before the commit finishes. If you do this, be sure to
|
||||
# call `enqueue_jobs` after the transaction is comitted.
|
||||
# hidden_reason_id - Reason for hiding the post (optional)
|
||||
#
|
||||
# When replying to a topic:
|
||||
# topic_id - topic we're replying to
|
||||
|
@ -62,6 +63,7 @@ class PostCreator
|
|||
opts[:title] = pg_clean_up(opts[:title]) if opts[:title] && opts[:title].include?("\u0000")
|
||||
opts[:raw] = pg_clean_up(opts[:raw]) if opts[:raw] && opts[:raw].include?("\u0000")
|
||||
opts.delete(:reply_to_post_number) unless opts[:topic_id]
|
||||
opts[:visible] = false if opts[:visible].nil? && opts[:hidden_reason_id].present?
|
||||
@guardian = opts[:guardian] if opts[:guardian]
|
||||
|
||||
@spam = false
|
||||
|
@ -454,6 +456,12 @@ class PostCreator
|
|||
post.custom_fields = fields
|
||||
end
|
||||
|
||||
if @opts[:hidden_reason_id].present?
|
||||
post.hidden = true
|
||||
post.hidden_at = Time.zone.now
|
||||
post.hidden_reason_id = @opts[:hidden_reason_id]
|
||||
end
|
||||
|
||||
@post = post
|
||||
end
|
||||
|
||||
|
|
|
@ -622,6 +622,51 @@ describe Email::Receiver do
|
|||
expect { process(:new_user) }.to change(Topic, :count)
|
||||
end
|
||||
|
||||
it "creates visible topic for ham" do
|
||||
SiteSetting.email_in_spam_header = 'none'
|
||||
|
||||
Fabricate(:user, email: "existing@bar.com", trust_level: SiteSetting.email_in_min_trust)
|
||||
expect { process(:existing_user) }.to change { Topic.count }.by(1) # Topic created
|
||||
|
||||
topic = Topic.last
|
||||
expect(topic.visible).to eq(true)
|
||||
|
||||
post = Post.last
|
||||
expect(post.hidden).to eq(false)
|
||||
expect(post.hidden_at).to eq(nil)
|
||||
expect(post.hidden_reason_id).to eq(nil)
|
||||
end
|
||||
|
||||
it "creates hidden topic for X-Spam-Flag" do
|
||||
SiteSetting.email_in_spam_header = 'X-Spam-Flag'
|
||||
|
||||
Fabricate(:user, email: "existing@bar.com", trust_level: SiteSetting.email_in_min_trust)
|
||||
expect { process(:spam_x_spam_flag) }.to change { Topic.count }.by(1) # Topic created
|
||||
|
||||
topic = Topic.last
|
||||
expect(topic.visible).to eq(false)
|
||||
|
||||
post = Post.last
|
||||
expect(post.hidden).to eq(true)
|
||||
expect(post.hidden_at).not_to eq(nil)
|
||||
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:email_spam_header_found])
|
||||
end
|
||||
|
||||
it "creates hidden topic for X-Spam-Status" do
|
||||
SiteSetting.email_in_spam_header = 'X-Spam-Status'
|
||||
|
||||
Fabricate(:user, email: "existing@bar.com", trust_level: SiteSetting.email_in_min_trust)
|
||||
expect { process(:spam_x_spam_status) }.to change { Topic.count }.by(1) # Topic created
|
||||
|
||||
topic = Topic.last
|
||||
expect(topic.visible).to eq(false)
|
||||
|
||||
post = Post.last
|
||||
expect(post.hidden).to eq(true)
|
||||
expect(post.hidden_at).not_to eq(nil)
|
||||
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:email_spam_header_found])
|
||||
end
|
||||
|
||||
it "adds the 'elided' part of the original message when always_show_trimmed_content is enabled" do
|
||||
SiteSetting.always_show_trimmed_content = true
|
||||
|
||||
|
|
|
@ -38,6 +38,15 @@ describe PostCreator do
|
|||
expect(post.wiki).to eq(true)
|
||||
end
|
||||
|
||||
it "can be created with a hidden reason" do
|
||||
hri = Post.hidden_reasons[:flag_threshold_reached]
|
||||
post = PostCreator.create(user, basic_topic_params.merge(hidden_reason_id: hri))
|
||||
expect(post.hidden).to eq(true)
|
||||
expect(post.hidden_at).to be_present
|
||||
expect(post.hidden_reason_id).to eq(hri)
|
||||
expect(post.topic.visible).to eq(false)
|
||||
end
|
||||
|
||||
it "ensures the user can create the topic" do
|
||||
Guardian.any_instance.expects(:can_create?).with(Topic, nil).returns(false)
|
||||
expect { creator.create }.to raise_error(Discourse::InvalidAccess)
|
||||
|
@ -71,6 +80,14 @@ describe PostCreator do
|
|||
context "success" do
|
||||
before { creator }
|
||||
|
||||
it "is not hidden" do
|
||||
p = creator.create
|
||||
expect(p.hidden).to eq(false)
|
||||
expect(p.hidden_at).not_to be_present
|
||||
expect(p.hidden_reason_id).to eq(nil)
|
||||
expect(p.topic.visible).to eq(true)
|
||||
end
|
||||
|
||||
it "doesn't return true for spam" do
|
||||
creator.create
|
||||
expect(creator.spam?).to eq(false)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
Return-Path: <existing@bar.com>
|
||||
From: Foo Bar <existing@bar.com>
|
||||
To: category@bar.com
|
||||
Subject: This is a topic from an existing user
|
||||
Date: Fri, 15 Jan 2016 00:12:43 +0100
|
||||
Message-ID: <32@foo.bar.mail>
|
||||
Mime-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
X-Spam-Flag: YES
|
||||
|
||||
Hey, this is a topic from an existing user ;)
|
|
@ -0,0 +1,12 @@
|
|||
Return-Path: <existing@bar.com>
|
||||
From: Foo Bar <existing@bar.com>
|
||||
To: category@bar.com
|
||||
Subject: This is a topic from an existing user
|
||||
Date: Fri, 15 Jan 2016 00:12:43 +0100
|
||||
Message-ID: <32@foo.bar.mail>
|
||||
Mime-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
X-Spam-Status: Yes, score=12.3 required=4.5
|
||||
|
||||
Hey, this is a topic from an existing user ;)
|
Loading…
Reference in New Issue