mirror of
https://github.com/discourse/discourse.git
synced 2024-12-05 03:33:44 +08:00
6a05f190c6
Previously we would create users, then destroy them at the end of the job if the post was rejected. Now we do not create users unless required.
1260 lines
50 KiB
Ruby
1260 lines
50 KiB
Ruby
require "rails_helper"
|
||
require "email/receiver"
|
||
|
||
describe Email::Receiver do
|
||
|
||
before do
|
||
SiteSetting.email_in = true
|
||
SiteSetting.reply_by_email_address = "reply+%{reply_key}@bar.com"
|
||
SiteSetting.alternative_reply_by_email_addresses = "alt+%{reply_key}@bar.com"
|
||
end
|
||
|
||
def process(email_name)
|
||
Email::Receiver.new(email(email_name)).process!
|
||
end
|
||
|
||
it "raises an EmptyEmailError when 'mail_string' is blank" do
|
||
expect { Email::Receiver.new(nil) }.to raise_error(Email::Receiver::EmptyEmailError)
|
||
expect { Email::Receiver.new("") }.to raise_error(Email::Receiver::EmptyEmailError)
|
||
end
|
||
|
||
it "raises a ScreenedEmailError when email address is screened" do
|
||
ScreenedEmail.expects(:should_block?).with("screened@mail.com").returns(true)
|
||
expect { process(:screened_email) }.to raise_error(Email::Receiver::ScreenedEmailError)
|
||
end
|
||
|
||
it "raises EmailNotAllowed when email address is not on whitelist" do
|
||
SiteSetting.email_domains_whitelist = "example.com|bar.com"
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
expect { process(:blacklist_whitelist_email) }.to raise_error(Email::Receiver::EmailNotAllowed)
|
||
end
|
||
|
||
it "raises EmailNotAllowed when email address is on blacklist" do
|
||
SiteSetting.email_domains_blacklist = "email.com|mail.com"
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
expect { process(:blacklist_whitelist_email) }.to raise_error(Email::Receiver::EmailNotAllowed)
|
||
end
|
||
|
||
it "raises an UserNotFoundError when staged users are disabled" do
|
||
SiteSetting.enable_staged_users = false
|
||
expect { process(:user_not_found) }.to raise_error(Email::Receiver::UserNotFoundError)
|
||
end
|
||
|
||
it "raises an AutoGeneratedEmailError when the mail is auto generated" do
|
||
expect { process(:auto_generated_precedence) }.to raise_error(Email::Receiver::AutoGeneratedEmailError)
|
||
expect { process(:auto_generated_header) }.to raise_error(Email::Receiver::AutoGeneratedEmailError)
|
||
end
|
||
|
||
it "raises a NoBodyDetectedError when the body is blank" do
|
||
expect { process(:no_body) }.to raise_error(Email::Receiver::NoBodyDetectedError)
|
||
end
|
||
|
||
it "raises a NoSenderDetectedError when the From header is missing" do
|
||
expect { process(:no_from) }.to raise_error(Email::Receiver::NoSenderDetectedError)
|
||
end
|
||
|
||
it "raises an InactiveUserError when the sender is inactive" do
|
||
Fabricate(:user, email: "inactive@bar.com", active: false)
|
||
expect { process(:inactive_sender) }.to raise_error(Email::Receiver::InactiveUserError)
|
||
end
|
||
|
||
it "raises a SilencedUserError when the sender has been silenced" do
|
||
Fabricate(:user, email: "silenced@bar.com", silenced_till: 1.year.from_now)
|
||
expect { process(:silenced_sender) }.to raise_error(Email::Receiver::SilencedUserError)
|
||
end
|
||
|
||
it "doesn't raise an InactiveUserError when the sender is staged" do
|
||
user = Fabricate(:user, email: "staged@bar.com", active: false, staged: true)
|
||
post = Fabricate(:post)
|
||
|
||
post_reply_key = Fabricate(:post_reply_key,
|
||
user: user,
|
||
post: post,
|
||
reply_key: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||
)
|
||
|
||
expect { process(:staged_sender) }.not_to raise_error
|
||
end
|
||
|
||
it "raises a BadDestinationAddress when destinations aren't matching any of the incoming emails" do
|
||
expect { process(:bad_destinations) }.to raise_error(Email::Receiver::BadDestinationAddress)
|
||
end
|
||
|
||
it "raises an OldDestinationError when notification is too old" do
|
||
SiteSetting.disallow_reply_by_email_after_days = 2
|
||
|
||
topic = Fabricate(:topic, id: 424242)
|
||
post = Fabricate(:post, topic: topic, id: 123456)
|
||
user = Fabricate(:user, email: "discourse@bar.com")
|
||
|
||
expect { process(:old_destination) }.to raise_error(
|
||
Email::Receiver::BadDestinationAddress
|
||
)
|
||
|
||
IncomingEmail.destroy_all
|
||
post.update!(created_at: 3.days.ago)
|
||
|
||
expect { process(:old_destination) }.to raise_error(
|
||
Email::Receiver::OldDestinationError
|
||
)
|
||
|
||
SiteSetting.disallow_reply_by_email_after_days = 0
|
||
IncomingEmail.destroy_all
|
||
|
||
expect { process(:old_destination) }.to raise_error(
|
||
Email::Receiver::BadDestinationAddress
|
||
)
|
||
end
|
||
|
||
context "bounces" do
|
||
it "raises a BouncerEmailError" do
|
||
expect { process(:bounced_email) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
expect(IncomingEmail.last.is_bounce).to eq(true)
|
||
|
||
expect { process(:bounced_email_multiple_status_codes) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
expect(IncomingEmail.last.is_bounce).to eq(true)
|
||
end
|
||
|
||
describe "creating whisper post in PMs for staged users" do
|
||
let(:email_address) { "linux-admin@b-s-c.co.jp" }
|
||
let(:user1) { user1 = Fabricate(:user) }
|
||
let(:user2) { user2 = Fabricate(:staged, email: email_address) }
|
||
let(:topic) { Fabricate(:topic, archetype: 'private_message', category_id: nil, user: user1, allowed_users: [user1, user2]) }
|
||
let(:post) { create_post(topic: topic, user: user1) }
|
||
|
||
before do
|
||
SiteSetting.enable_staged_users = true
|
||
SiteSetting.enable_whispers = true
|
||
end
|
||
|
||
def create_post_reply_key(value)
|
||
Fabricate(:post_reply_key,
|
||
reply_key: value,
|
||
user: user2,
|
||
post: post
|
||
)
|
||
end
|
||
|
||
it "when bounce without verp" do
|
||
create_post_reply_key("4f97315cc828096c9cb34c6f1a0d6fe8")
|
||
|
||
expect { process(:bounced_email) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
post = Post.last
|
||
expect(post.whisper?).to eq(true)
|
||
expect(post.raw).to eq(I18n.t("system_messages.email_bounced", email: email_address, raw: "Your email bounced").strip)
|
||
expect(IncomingEmail.last.is_bounce).to eq(true)
|
||
end
|
||
|
||
it "when bounce with verp" do
|
||
SiteSetting.reply_by_email_address = "foo+%{reply_key}@discourse.org"
|
||
bounce_key = "14b08c855160d67f2e0c2f8ef36e251e"
|
||
create_post_reply_key(bounce_key)
|
||
Fabricate(:email_log, to_address: email_address, user: user2, bounce_key: bounce_key, post: post)
|
||
|
||
expect { process(:hard_bounce_via_verp) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
post = Post.last
|
||
expect(post.whisper?).to eq(true)
|
||
expect(post.raw).to eq(I18n.t("system_messages.email_bounced", email: email_address, raw: "Your email bounced").strip)
|
||
expect(IncomingEmail.last.is_bounce).to eq(true)
|
||
end
|
||
end
|
||
end
|
||
|
||
it "logs a blank error" do
|
||
Email::Receiver.any_instance.stubs(:process_internal).raises(RuntimeError, "")
|
||
process(:existing_user) rescue RuntimeError
|
||
expect(IncomingEmail.last.error).to eq("RuntimeError")
|
||
end
|
||
|
||
it "matches the correct user" do
|
||
user = Fabricate(:user)
|
||
email_log = Fabricate(:email_log, to_address: user.email, user: user, bounce_key: nil)
|
||
email, name = Email::Receiver.new(email(:existing_user)).parse_from_field
|
||
expect(email).to eq("existing@bar.com")
|
||
expect(name).to eq("Foo Bar")
|
||
end
|
||
|
||
it "strips null bytes from the subject" do
|
||
expect do
|
||
process(:null_byte_in_subject)
|
||
end.to raise_error(Email::Receiver::BadDestinationAddress)
|
||
end
|
||
|
||
context "bounces to VERP" do
|
||
|
||
let(:bounce_key) { "14b08c855160d67f2e0c2f8ef36e251e" }
|
||
let(:bounce_key_2) { "b542fb5a9bacda6d28cc061d18e4eb83" }
|
||
let!(:user) { Fabricate(:user, email: "linux-admin@b-s-c.co.jp") }
|
||
let!(:email_log) { Fabricate(:email_log, to_address: user.email, user: user, bounce_key: bounce_key) }
|
||
let!(:email_log_2) { Fabricate(:email_log, to_address: user.email, user: user, bounce_key: bounce_key_2) }
|
||
|
||
it "deals with soft bounces" do
|
||
expect { process(:soft_bounce_via_verp) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
|
||
email_log.reload
|
||
expect(email_log.bounced).to eq(true)
|
||
expect(email_log.user.user_stat.bounce_score).to eq(SiteSetting.soft_bounce_score)
|
||
end
|
||
|
||
it "deals with hard bounces" do
|
||
expect { process(:hard_bounce_via_verp) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
|
||
email_log.reload
|
||
expect(email_log.bounced).to eq(true)
|
||
expect(email_log.user.user_stat.bounce_score).to eq(SiteSetting.hard_bounce_score)
|
||
|
||
expect { process(:hard_bounce_via_verp_2) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
|
||
email_log_2.reload
|
||
expect(email_log_2.user.user_stat.bounce_score).to eq(SiteSetting.hard_bounce_score * 2)
|
||
expect(email_log_2.bounced).to eq(true)
|
||
end
|
||
|
||
it "sends a system message once they reach the 'bounce_score_threshold'" do
|
||
expect(user.active).to eq(true)
|
||
|
||
user.user_stat.bounce_score = SiteSetting.bounce_score_threshold - 1
|
||
user.user_stat.save!
|
||
|
||
SystemMessage.expects(:create_from_system_user).with(user, :email_revoked)
|
||
|
||
expect { process(:hard_bounce_via_verp) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
end
|
||
|
||
it "automatically deactive users once they reach the 'bounce_score_threshold_deactivate' threshold" do
|
||
expect(user.active).to eq(true)
|
||
|
||
user.user_stat.bounce_score = SiteSetting.bounce_score_threshold_deactivate - 1
|
||
user.user_stat.save!
|
||
|
||
expect { process(:soft_bounce_via_verp) }.to raise_error(Email::Receiver::BouncedEmailError)
|
||
|
||
user.reload
|
||
email_log.reload
|
||
|
||
expect(email_log.bounced).to eq(true)
|
||
expect(user.active).to eq(false)
|
||
end
|
||
|
||
end
|
||
|
||
context "reply" do
|
||
|
||
let(:reply_key) { "4f97315cc828096c9cb34c6f1a0d6fe8" }
|
||
let(:category) { Fabricate(:category) }
|
||
let(:user) { Fabricate(:user, email: "discourse@bar.com") }
|
||
let(:topic) { create_topic(category: category, user: user) }
|
||
let(:post) { create_post(topic: topic) }
|
||
|
||
let!(:post_reply_key) do
|
||
Fabricate(:post_reply_key,
|
||
reply_key: reply_key,
|
||
user: user,
|
||
post: post
|
||
)
|
||
end
|
||
|
||
let :topic_user do
|
||
TopicUser.find_by(topic_id: topic.id, user_id: user.id)
|
||
end
|
||
|
||
it "uses MD5 of 'mail_string' there is no message_id" do
|
||
mail_string = email(:missing_message_id)
|
||
expect { Email::Receiver.new(mail_string).process! }.to change { IncomingEmail.count }
|
||
expect(IncomingEmail.last.message_id).to eq(Digest::MD5.hexdigest(mail_string))
|
||
end
|
||
|
||
it "raises a ReplyUserNotMatchingError when the email address isn't matching the one we sent the notification to" do
|
||
Fabricate(:user, email: "someone_else@bar.com")
|
||
expect { process(:reply_user_not_matching) }.to raise_error(Email::Receiver::ReplyUserNotMatchingError)
|
||
end
|
||
|
||
it "raises a FromReplyByAddressError when the email is from the reply by email address" do
|
||
expect { process(:from_reply_by_email_address) }.to raise_error(Email::Receiver::FromReplyByAddressError)
|
||
end
|
||
|
||
it "accepts reply from secondary email address" do
|
||
Fabricate(:secondary_email, email: "someone_else@bar.com", user: user)
|
||
|
||
expect { process(:reply_user_not_matching) }
|
||
.to change { topic.posts.count }
|
||
|
||
post = Post.last
|
||
|
||
expect(post.raw).to eq(
|
||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||
)
|
||
|
||
expect(post.user).to eq(user)
|
||
end
|
||
|
||
it "raises a TopicNotFoundError when the topic was deleted" do
|
||
topic.update_columns(deleted_at: 1.day.ago)
|
||
expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicNotFoundError)
|
||
end
|
||
|
||
context "a closed topic" do
|
||
|
||
before do
|
||
topic.update_columns(closed: true)
|
||
end
|
||
|
||
it "raises a TopicClosedError when the topic was closed" do
|
||
expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicClosedError)
|
||
end
|
||
|
||
it "Can watch topics via the watch command" do
|
||
# TODO support other locales as well, the tricky thing is that these string live in
|
||
# client.yml not on server yml so it is a bit tricky to find
|
||
|
||
topic.update_columns(closed: true)
|
||
process(:watch)
|
||
expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:watching])
|
||
end
|
||
|
||
it "Can mute topics via the mute command" do
|
||
process(:mute)
|
||
expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:muted])
|
||
end
|
||
|
||
it "can track a topic via the track command" do
|
||
process(:track)
|
||
expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:tracking])
|
||
end
|
||
end
|
||
|
||
it "raises an InvalidPost when there was an error while creating the post" do
|
||
expect { process(:too_small) }.to raise_error(Email::Receiver::TooShortPost)
|
||
end
|
||
|
||
it "raises an InvalidPost when there are too may mentions" do
|
||
SiteSetting.max_mentions_per_post = 1
|
||
Fabricate(:user, username: "user1")
|
||
Fabricate(:user, username: "user2")
|
||
expect { process(:too_many_mentions) }.to raise_error(Email::Receiver::InvalidPost)
|
||
end
|
||
|
||
it "raises an InvalidPostAction when they aren't allowed to like a post" do
|
||
topic.update_columns(archived: true)
|
||
expect { process(:like) }.to raise_error(Email::Receiver::InvalidPostAction)
|
||
end
|
||
|
||
it "works" do
|
||
expect { process(:text_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a text reply :)\n\nEmail parsing should not break because of a UTF-8 character: ’")
|
||
expect(topic.posts.last.via_email).to eq(true)
|
||
expect(topic.posts.last.cooked).not_to match(/<br/)
|
||
|
||
expect { process(:html_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a **HTML** reply ;)")
|
||
end
|
||
|
||
it "automatically elides gmail quotes" do
|
||
SiteSetting.always_show_trimmed_content = true
|
||
expect { process(:gmail_html_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a **GMAIL** reply ;)\n\n<details class='elided'>\n<summary title='Show trimmed content'>···</summary>\n\nThis is the *elided* part!\n\n</details>")
|
||
end
|
||
|
||
it "doesn't process email with same message-id more than once" do
|
||
expect do
|
||
process(:text_reply)
|
||
process(:text_reply)
|
||
end.to change { topic.posts.count }.by(1)
|
||
end
|
||
|
||
it "handles different encodings correctly" do
|
||
expect { process(:hebrew_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("שלום! מה שלומך היום?")
|
||
|
||
expect { process(:chinese_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("您好! 你今天好吗?")
|
||
|
||
expect { process(:reply_with_weird_encoding) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a reply with a weird encoding.")
|
||
|
||
expect { process(:reply_with_8bit_encoding) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("hab vergessen kritische zeichen einzufügen:\näöüÄÖÜß")
|
||
end
|
||
|
||
it "prefers text over html when site setting is disabled" do
|
||
SiteSetting.incoming_email_prefer_html = false
|
||
expect { process(:text_and_html_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is the *text* part.")
|
||
end
|
||
|
||
it "prefers html over text when site setting is enabled" do
|
||
SiteSetting.incoming_email_prefer_html = true
|
||
expect { process(:text_and_html_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq('This is the **html** part.')
|
||
end
|
||
|
||
it "uses text when prefer_html site setting is enabled but no html is available" do
|
||
SiteSetting.incoming_email_prefer_html = true
|
||
expect { process(:text_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a text reply :)\n\nEmail parsing should not break because of a UTF-8 character: ’")
|
||
end
|
||
|
||
it "removes the 'on <date>, <contact> wrote' quoting line" do
|
||
expect { process(:on_date_contact_wrote) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is the actual reply.")
|
||
end
|
||
|
||
it "removes the 'Previous Replies' marker" do
|
||
expect { process(:previous_replies) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This will not include the previous discussion that is present in this email.")
|
||
end
|
||
|
||
it "handles multiple paragraphs" do
|
||
expect { process(:paragraphs) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("Do you like liquorice?\n\nI really like them. One could even say that I am *addicted* to liquorice. Anf if\nyou can mix it up with some anise, then I'm in heaven ;)")
|
||
end
|
||
|
||
it "handles invalid from header" do
|
||
expect { process(:invalid_from_1) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This email was sent with an invalid from header field.")
|
||
end
|
||
|
||
it "raises a NoSenderDetectedError when the From header doesn't contain an email address" do
|
||
expect { process(:invalid_from_2) }.to raise_error(Email::Receiver::NoSenderDetectedError)
|
||
end
|
||
|
||
it "doesn't raise an AutoGeneratedEmailError when the mail is auto generated but is whitelisted" do
|
||
SiteSetting.auto_generated_whitelist = "foo@bar.com|discourse@bar.com"
|
||
expect { process(:auto_generated_whitelisted) }.to change { topic.posts.count }
|
||
end
|
||
|
||
it "doesn't raise an AutoGeneratedEmailError when block_auto_generated_emails is disabled" do
|
||
SiteSetting.block_auto_generated_emails = false
|
||
expect { process(:auto_generated_unblocked) }.to change { topic.posts.count }
|
||
end
|
||
|
||
it "allows staged users to reply to a restricted category" do
|
||
user.update_columns(staged: true)
|
||
|
||
category.email_in = "category@bar.com"
|
||
category.email_in_allow_strangers = true
|
||
category.set_permissions(Group[:trust_level_4] => :full)
|
||
category.save
|
||
|
||
expect { process(:staged_reply_restricted) }.to change { topic.posts.count }
|
||
end
|
||
|
||
it "posts a reply to the topic when the post was deleted" do
|
||
post.update_columns(deleted_at: 1.day.ago)
|
||
expect { process(:reply_user_matching) }.to change { topic.posts.count }
|
||
expect(topic.ordered_posts.last.reply_to_post_number).to be_nil
|
||
end
|
||
|
||
describe 'Unsubscribing via email' do
|
||
let(:last_email) { ActionMailer::Base.deliveries.last }
|
||
|
||
describe 'unsubscribe_subject.eml' do
|
||
it 'sends an email asking the user to confirm the unsubscription' do
|
||
expect { process("unsubscribe_subject") }.to change { ActionMailer::Base.deliveries.count }.by(1)
|
||
expect(last_email.to.length).to eq 1
|
||
expect(last_email.from.length).to eq 1
|
||
expect(last_email.from).to include "noreply@#{Discourse.current_hostname}"
|
||
expect(last_email.to).to include "discourse@bar.com"
|
||
expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title)
|
||
end
|
||
|
||
it 'does nothing unless unsubscribe_via_email is turned on' do
|
||
SiteSetting.unsubscribe_via_email = false
|
||
before_deliveries = ActionMailer::Base.deliveries.count
|
||
expect { process("unsubscribe_subject") }.to raise_error { Email::Receiver::BadDestinationAddress }
|
||
expect(before_deliveries).to eq ActionMailer::Base.deliveries.count
|
||
end
|
||
end
|
||
|
||
describe 'unsubscribe_body.eml' do
|
||
it 'sends an email asking the user to confirm the unsubscription' do
|
||
expect { process("unsubscribe_body") }.to change { ActionMailer::Base.deliveries.count }.by(1)
|
||
expect(last_email.to.length).to eq 1
|
||
expect(last_email.from.length).to eq 1
|
||
expect(last_email.from).to include "noreply@#{Discourse.current_hostname}"
|
||
expect(last_email.to).to include "discourse@bar.com"
|
||
expect(last_email.subject).to eq I18n.t(:"unsubscribe_mailer.subject_template").gsub("%{site_title}", SiteSetting.title)
|
||
end
|
||
|
||
it 'does nothing unless unsubscribe_via_email is turned on' do
|
||
SiteSetting.unsubscribe_via_email = false
|
||
before_deliveries = ActionMailer::Base.deliveries.count
|
||
expect { process("unsubscribe_body") }.to raise_error { Email::Receiver::InvalidPost }
|
||
expect(before_deliveries).to eq ActionMailer::Base.deliveries.count
|
||
end
|
||
end
|
||
|
||
it "raises an UnsubscribeNotAllowed and does not send an unsubscribe email" do
|
||
before_deliveries = ActionMailer::Base.deliveries.count
|
||
expect { process(:unsubscribe_new_user) }.to raise_error { Email::Receiver::UnsubscribeNotAllowed }
|
||
expect(before_deliveries).to eq ActionMailer::Base.deliveries.count
|
||
end
|
||
end
|
||
|
||
it "handles inline reply" do
|
||
expect { process(:inline_reply) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("And this is *my* reply :+1:")
|
||
end
|
||
|
||
it "retrieves the first part of multiple replies" do
|
||
expect { process(:inline_mixed_replies) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("> WAT <https://bar.com/users/wat> November 28\n>\n> This is the previous post.\n\nAnd this is *my* reply :+1:\n\n> This is another post.\n\nAnd this is **another** reply.")
|
||
end
|
||
|
||
it "strips mobile/webmail signatures" do
|
||
expect { process(:iphone_signature) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is not the signature you're looking for.")
|
||
end
|
||
|
||
it "strips 'original message' context" do
|
||
expect { process(:original_message) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a reply :)")
|
||
end
|
||
|
||
it "add the 'elided' part of the original message only for private messages" do
|
||
topic.update_columns(category_id: nil, archetype: Archetype.private_message)
|
||
topic.allowed_users << user
|
||
topic.save
|
||
|
||
expect { process(:original_message) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to eq("This is a reply :)\n\n<details class='elided'>\n<summary title='Show trimmed content'>···</summary>\n\n---Original Message---\nThis part should not be included\n\n</details>")
|
||
end
|
||
|
||
it "doesn't include the 'elided' part of the original message when always_show_trimmed_content is disabled" do
|
||
SiteSetting.always_show_trimmed_content = false
|
||
expect { process(:original_message) }.to change { topic.posts.count }.from(1).to(2)
|
||
expect(topic.posts.last.raw).to eq("This is a reply :)")
|
||
end
|
||
|
||
it "adds the 'elided' part of the original message for public replies when always_show_trimmed_content is enabled" do
|
||
SiteSetting.always_show_trimmed_content = true
|
||
expect { process(:original_message) }.to change { topic.posts.count }.from(1).to(2)
|
||
expect(topic.posts.last.raw).to eq("This is a reply :)\n\n<details class='elided'>\n<summary title='Show trimmed content'>···</summary>\n\n---Original Message---\nThis part should not be included\n\n</details>")
|
||
end
|
||
|
||
it "supports attached images in TEXT part" do
|
||
SiteSetting.incoming_email_prefer_html = false
|
||
|
||
expect { process(:no_body_with_image) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to match(/<img/)
|
||
|
||
expect { process(:inline_image) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to match(/Before\s+<img.+>\s+After/)
|
||
end
|
||
|
||
it "supports attached images in HTML part" do
|
||
SiteSetting.incoming_email_prefer_html = true
|
||
|
||
expect { process(:inline_image) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.raw).to match(/\*\*Before\*\*\s+<img.+>\s+\*After\*/)
|
||
end
|
||
|
||
it "supports attachments" do
|
||
SiteSetting.authorized_extensions = "txt|jpg"
|
||
expect { process(:attached_txt_file) }.to change { topic.posts.count }
|
||
post = topic.posts.last
|
||
expect(post.raw).to match(/\APlease find some text file attached\.\s+<a class='attachment' href='\/uploads\/default\/original\/.+?txt'>text\.txt<\/a> \(20 Bytes\)\z/)
|
||
expect(post.uploads.size).to eq 1
|
||
|
||
expect { process(:apple_mail_attachment) }.to change { topic.posts.count }
|
||
post = topic.posts.last
|
||
expect(post.raw).to match(/\APicture below\.\s+<img.+?src="\/uploads\/default\/original\/.+?jpeg" class="">\s+Picture above\.\z/)
|
||
expect(post.uploads.size).to eq 1
|
||
end
|
||
|
||
it "supports eml attachments" do
|
||
SiteSetting.authorized_extensions = "eml"
|
||
expect { process(:attached_eml_file) }.to change { topic.posts.count }
|
||
post = topic.posts.last
|
||
expect(post.raw).to match(/\APlease find the eml file attached\.\s+<a class='attachment' href='\/uploads\/default\/original\/.+?eml'>sample\.eml<\/a> \(193 Bytes\)\z/)
|
||
expect(post.uploads.size).to eq 1
|
||
end
|
||
|
||
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")
|
||
expect(topic.posts.last.uploads.size).to eq 0
|
||
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 }
|
||
expect(topic.posts.last.uploads.size).to eq 0
|
||
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 }
|
||
post = topic.posts.last
|
||
expect(post.raw).to match(/#{missing_attachment_regex}/)
|
||
expect(post.uploads.size).to eq 0
|
||
end
|
||
end
|
||
|
||
it "supports emails with just an attachment" do
|
||
SiteSetting.authorized_extensions = "pdf"
|
||
expect { process(:attached_pdf_file) }.to change { topic.posts.count }
|
||
post = topic.posts.last
|
||
expect(post.raw).to match(/\A\s+<a class='attachment' href='\/uploads\/default\/original\/.+?pdf'>discourse\.pdf<\/a> \(64 KB\)\z/)
|
||
expect(post.uploads.size).to eq 1
|
||
end
|
||
|
||
it "supports liking via email" do
|
||
expect { process(:like) }.to change(PostAction, :count)
|
||
end
|
||
|
||
it "ensures posts aren't dated in the future" do
|
||
expect { process(:from_the_future) }.to change { topic.posts.count }
|
||
expect(topic.posts.last.created_at).to be_within(1.minute).of(DateTime.now)
|
||
end
|
||
|
||
it "accepts emails with wrong reply key if the system knows about the forwarded email" do
|
||
Fabricate(:user, email: "bob@bar.com")
|
||
Fabricate(:incoming_email,
|
||
raw: <<~RAW,
|
||
Return-Path: <discourse@bar.com>
|
||
From: Alice <discourse@bar.com>
|
||
To: dave@bar.com, reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com
|
||
CC: carol@bar.com, bob@bar.com
|
||
Subject: Hello world
|
||
Date: Fri, 15 Jan 2016 00:12:43 +0100
|
||
Message-ID: <10@foo.bar.mail>
|
||
Mime-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: quoted-printable
|
||
|
||
This post was created by email.
|
||
RAW
|
||
from_address: "discourse@bar.com",
|
||
to_addresses: "dave@bar.com;reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com",
|
||
cc_addresses: "carol@bar.com;bob@bar.com",
|
||
topic: topic,
|
||
post: post,
|
||
user: user)
|
||
|
||
expect { process(:reply_user_not_matching_but_known) }.to change { topic.posts.count }
|
||
end
|
||
|
||
it "re-enables user's PM email notifications when user replies to a private topic" do
|
||
topic.update_columns(category_id: nil, archetype: Archetype.private_message)
|
||
topic.allowed_users << user
|
||
topic.save
|
||
|
||
user.user_option.update_columns(email_messages_level: UserOption.email_level_types[:never])
|
||
expect { process(:reply_user_matching) }.to change { topic.posts.count }
|
||
user.reload
|
||
expect(user.user_option.email_messages_level).to eq(UserOption.email_level_types[:always])
|
||
end
|
||
|
||
end
|
||
|
||
context "new message to a group" do
|
||
|
||
let!(:group) { Fabricate(:group, incoming_email: "team@bar.com|meat@bar.com") }
|
||
|
||
it "handles encoded display names" do
|
||
expect { process(:encoded_display_name) }.to change(Topic, :count)
|
||
|
||
topic = Topic.last
|
||
expect(topic.title).to eq("I need help")
|
||
expect(topic.private_message?).to eq(true)
|
||
expect(topic.allowed_groups).to include(group)
|
||
|
||
user = topic.user
|
||
expect(user.staged).to eq(true)
|
||
expect(user.username).to eq("random.name")
|
||
expect(user.name).to eq("Случайная Имя")
|
||
end
|
||
|
||
it "handles email with no subject" do
|
||
expect { process(:no_subject) }.to change(Topic, :count)
|
||
expect(Topic.last.title).to eq("This topic needs a title")
|
||
end
|
||
|
||
it "invites everyone in the chain but emails configured as 'incoming' (via reply, group or category)" do
|
||
expect { process(:cc) }.to change(Topic, :count)
|
||
|
||
topic = Topic.last
|
||
|
||
emails = topic.allowed_users.joins(:user_emails).pluck(:"user_emails.email")
|
||
expect(emails).to contain_exactly("someone@else.com", "discourse@bar.com", "wat@bar.com")
|
||
|
||
expect(topic.topic_users.count).to eq(3)
|
||
end
|
||
|
||
it "invites users with a secondary email in the chain" do
|
||
user1 = Fabricate(:user,
|
||
trust_level: SiteSetting.email_in_min_trust,
|
||
user_emails: [
|
||
Fabricate.build(:secondary_email, email: "discourse@bar.com"),
|
||
Fabricate.build(:secondary_email, email: "someone@else.com"),
|
||
]
|
||
)
|
||
|
||
user2 = Fabricate(:user,
|
||
trust_level: SiteSetting.email_in_min_trust,
|
||
user_emails: [
|
||
Fabricate.build(:secondary_email, email: "team@bar.com"),
|
||
Fabricate.build(:secondary_email, email: "wat@bar.com"),
|
||
]
|
||
)
|
||
|
||
expect { process(:cc) }.to change(Topic, :count)
|
||
expect(Topic.last.allowed_users).to contain_exactly(user1, user2)
|
||
end
|
||
|
||
it "cap the number of staged users created per email" do
|
||
SiteSetting.maximum_staged_users_per_email = 1
|
||
expect { process(:cc) }.to change(Topic, :count)
|
||
expect(Topic.last.ordered_posts[-1].post_type).to eq(Post.types[:moderator_action])
|
||
end
|
||
|
||
describe "when 'find_related_post_with_key' is disabled" do
|
||
before do
|
||
SiteSetting.find_related_post_with_key = false
|
||
end
|
||
|
||
it "associates email replies using both 'In-Reply-To' and 'References' headers" do
|
||
expect { process(:email_reply_1) }
|
||
.to change(Topic, :count).by(1) & change(Post, :count).by(3)
|
||
|
||
topic = Topic.last
|
||
ordered_posts = topic.ordered_posts
|
||
|
||
expect(ordered_posts.first.raw).to eq('This is email reply **1**.')
|
||
|
||
ordered_posts[1..-1].each do |post|
|
||
expect(post.action_code).to eq('invited_user')
|
||
expect(post.user.email).to eq('one@foo.com')
|
||
|
||
expect(%w{two three}.include?(post.custom_fields["action_code_who"]))
|
||
.to eq(true)
|
||
end
|
||
|
||
expect { process(:email_reply_2) }.to change { topic.posts.count }.by(1)
|
||
expect { process(:email_reply_3) }.to change { topic.posts.count }.by(1)
|
||
ordered_posts[1..-1].each(&:trash!)
|
||
expect { process(:email_reply_4) }.to change { topic.posts.count }.by(1)
|
||
end
|
||
end
|
||
|
||
it "supports any kind of attachments when 'allow_all_attachments_for_group_messages' is enabled" do
|
||
SiteSetting.allow_all_attachments_for_group_messages = true
|
||
expect { process(:attached_rb_file) }.to change(Topic, :count)
|
||
expect(Post.last.raw).to match(/<a\sclass='attachment'[^>]*>discourse\.rb<\/a>/)
|
||
expect(Post.last.uploads.length).to eq 1
|
||
end
|
||
|
||
it "reenables user's PM email notifications when user emails new topic to group" do
|
||
user = Fabricate(:user, email: "existing@bar.com")
|
||
user.user_option.update_columns(email_messages_level: UserOption.email_level_types[:never])
|
||
expect { process(:group_existing_user) }.to change(Topic, :count)
|
||
user.reload
|
||
expect(user.user_option.email_messages_level).to eq(UserOption.email_level_types[:always])
|
||
end
|
||
|
||
context "with forwarded emails enabled" do
|
||
before do
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
SiteSetting.enable_forwarded_emails = true
|
||
end
|
||
|
||
it "handles forwarded emails" do
|
||
expect { process(:forwarded_email_1) }.to change(Topic, :count)
|
||
|
||
forwarded_post, last_post = *Post.last(2)
|
||
|
||
expect(forwarded_post.user.email).to eq("some@one.com")
|
||
expect(last_post.user.email).to eq("ba@bar.com")
|
||
|
||
expect(forwarded_post.raw).to match(/XoXo/)
|
||
expect(last_post.raw).to match(/can you have a look at this email below/)
|
||
|
||
expect(last_post.post_type).to eq(Post.types[:regular])
|
||
end
|
||
|
||
it "handles weirdly forwarded emails" do
|
||
group.add(Fabricate(:user, email: "ba@bar.com"))
|
||
group.save
|
||
|
||
SiteSetting.enable_forwarded_emails = true
|
||
expect { process(:forwarded_email_2) }.to change(Topic, :count)
|
||
|
||
forwarded_post, last_post = *Post.last(2)
|
||
|
||
expect(forwarded_post.user.email).to eq("some@one.com")
|
||
expect(last_post.user.email).to eq("ba@bar.com")
|
||
|
||
expect(forwarded_post.raw).to match(/XoXo/)
|
||
expect(last_post.raw).to match(/can you have a look at this email below/)
|
||
|
||
expect(last_post.post_type).to eq(Post.types[:whisper])
|
||
end
|
||
|
||
# Who thought this was a good idea?!
|
||
it "doesn't blow up with localized email headers" do
|
||
expect { process(:forwarded_email_3) }.to change(Topic, :count)
|
||
end
|
||
|
||
end
|
||
|
||
context "when message sent to a group has no key and find_related_post_with_key is enabled" do
|
||
let!(:topic) do
|
||
process(:email_reply_1)
|
||
Topic.last
|
||
end
|
||
|
||
it "creates a reply when the sender and referenced message id are known" do
|
||
expect { process(:email_reply_2) }.to change { topic.posts.count }.by(1).and change { Topic.count }.by(0)
|
||
end
|
||
|
||
it "creates a new topic when the sender is not known" do
|
||
IncomingEmail.where(message_id: '34@foo.bar.mail').update(cc_addresses: 'three@foo.com')
|
||
expect { process(:email_reply_2) }.to change { topic.posts.count }.by(0).and change { Topic.count }.by(1)
|
||
end
|
||
|
||
it "creates a new topic when the referenced message id is not known" do
|
||
IncomingEmail.where(message_id: '34@foo.bar.mail').update(message_id: '99@foo.bar.mail')
|
||
expect { process(:email_reply_2) }.to change { topic.posts.count }.by(0).and change { Topic.count }.by(1)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "new topic in a category" do
|
||
|
||
let!(:category) { Fabricate(:category, email_in: "category@bar.com|category@foo.com", email_in_allow_strangers: false) }
|
||
|
||
it "raises a StrangersNotAllowedError when 'email_in_allow_strangers' is disabled" do
|
||
expect { process(:new_user) }.to raise_error(Email::Receiver::StrangersNotAllowedError)
|
||
end
|
||
|
||
it "raises an InsufficientTrustLevelError when user's trust level isn't enough" do
|
||
Fabricate(:user, email: "existing@bar.com", trust_level: 3)
|
||
SiteSetting.email_in_min_trust = 4
|
||
expect { process(:existing_user) }.to raise_error(Email::Receiver::InsufficientTrustLevelError)
|
||
end
|
||
|
||
it "works" do
|
||
user = Fabricate(:user, email: "existing@bar.com", trust_level: SiteSetting.email_in_min_trust)
|
||
group = Fabricate(:group)
|
||
|
||
group.add(user)
|
||
group.save
|
||
|
||
category.set_permissions(group => :create_post)
|
||
category.save
|
||
|
||
# raises an InvalidAccess when the user doesn't have the privileges to create a topic
|
||
expect { process(:existing_user) }.to raise_error(Discourse::InvalidAccess)
|
||
|
||
category.update_columns(email_in_allow_strangers: true)
|
||
|
||
# allows new user to create a topic
|
||
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
|
||
|
||
Fabricate(:user, email: "existing@bar.com", trust_level: SiteSetting.email_in_min_trust)
|
||
expect { process(:forwarded_email_to_category) }.to change { Topic.count }.by(1) # Topic created
|
||
|
||
new_post, = Post.last
|
||
expect(new_post.raw).to include("Hi everyone, can you have a look at the email below?", "<summary title='Show trimmed content'>···</summary>", "Discoursing much today?")
|
||
end
|
||
|
||
it "works when approving is enabled" do
|
||
SiteSetting.approve_unless_trust_level = 4
|
||
|
||
Fabricate(:user, email: "tl3@bar.com", trust_level: TrustLevel[3])
|
||
Fabricate(:user, email: "tl4@bar.com", trust_level: TrustLevel[4])
|
||
|
||
category.set_permissions(Group[:trust_level_4] => :full)
|
||
category.save
|
||
|
||
Group.refresh_automatic_group!(:trust_level_4)
|
||
|
||
expect { process(:tl3_user) }.to raise_error(Email::Receiver::InvalidPost)
|
||
expect { process(:tl4_user) }.to change(Topic, :count)
|
||
end
|
||
|
||
it "ignores by case-insensitive title" do
|
||
SiteSetting.ignore_by_title = "foo"
|
||
expect { process(:ignored) }.to_not change(Topic, :count)
|
||
end
|
||
|
||
it "associates email from a secondary address with user" do
|
||
user = Fabricate(:user,
|
||
trust_level: SiteSetting.email_in_min_trust,
|
||
user_emails: [
|
||
Fabricate.build(:secondary_email, email: "existing@bar.com")
|
||
]
|
||
)
|
||
|
||
expect { process(:existing_user) }.to change(Topic, :count).by(1)
|
||
|
||
topic = Topic.last
|
||
|
||
expect(topic.posts.last.raw)
|
||
.to eq("Hey, this is a topic from an existing user ;)")
|
||
|
||
expect(topic.user).to eq(user)
|
||
end
|
||
end
|
||
|
||
context "new topic in a category that allows strangers" do
|
||
|
||
let!(:category) { Fabricate(:category, email_in: "category@bar.com|category@foo.com", email_in_allow_strangers: true) }
|
||
|
||
it "lets an email in from a stranger" do
|
||
expect { process(:new_user) }.to change(Topic, :count)
|
||
end
|
||
|
||
it "lets an email in from a high-TL user" do
|
||
Fabricate(:user, email: "tl4@bar.com", trust_level: TrustLevel[4])
|
||
expect { process(:tl4_user) }.to change(Topic, :count)
|
||
end
|
||
|
||
it "fails on email from a low-TL user" do
|
||
SiteSetting.email_in_min_trust = 4
|
||
Fabricate(:user, email: "tl3@bar.com", trust_level: TrustLevel[3])
|
||
expect { process(:tl3_user) }.to raise_error(Email::Receiver::InsufficientTrustLevelError)
|
||
end
|
||
|
||
end
|
||
|
||
context "#reply_by_email_address_regex" do
|
||
|
||
before do
|
||
SiteSetting.reply_by_email_address = nil
|
||
SiteSetting.alternative_reply_by_email_addresses = nil
|
||
end
|
||
|
||
it "it maches nothing if there is not reply_by_email_address" do
|
||
expect(Email::Receiver.reply_by_email_address_regex).to eq(/$a/)
|
||
end
|
||
|
||
it "uses 'reply_by_email_address' site setting" do
|
||
SiteSetting.reply_by_email_address = "foo+%{reply_key}@bar.com"
|
||
expect(Email::Receiver.reply_by_email_address_regex).to eq(/foo\+?(\h{32})?@bar\.com/)
|
||
end
|
||
|
||
it "uses 'alternative_reply_by_email_addresses' site setting" do
|
||
SiteSetting.alternative_reply_by_email_addresses = "alt.foo+%{reply_key}@bar.com"
|
||
expect(Email::Receiver.reply_by_email_address_regex).to eq(/alt\.foo\+?(\h{32})?@bar\.com/)
|
||
end
|
||
|
||
it "combines both 'reply_by_email' settings" do
|
||
SiteSetting.reply_by_email_address = "foo+%{reply_key}@bar.com"
|
||
SiteSetting.alternative_reply_by_email_addresses = "alt.foo+%{reply_key}@bar.com"
|
||
expect(Email::Receiver.reply_by_email_address_regex).to eq(/foo\+?(\h{32})?@bar\.com|alt\.foo\+?(\h{32})?@bar\.com/)
|
||
end
|
||
|
||
end
|
||
|
||
context "check_address" do
|
||
before do
|
||
SiteSetting.reply_by_email_address = "foo+%{reply_key}@bar.com"
|
||
end
|
||
|
||
it "returns nil when the key is invalid" do
|
||
expect(Email::Receiver.check_address('fake@fake.com')).to be_nil
|
||
expect(Email::Receiver.check_address('foo+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com')).to be_nil
|
||
end
|
||
|
||
context "with a valid reply" do
|
||
it "returns the destination when the key is valid" do
|
||
post_reply_key = Fabricate(:post_reply_key,
|
||
reply_key: '4f97315cc828096c9cb34c6f1a0d6fe8'
|
||
)
|
||
|
||
dest = Email::Receiver.check_address('foo+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com')
|
||
|
||
expect(dest).to be_present
|
||
expect(dest[:type]).to eq(:reply)
|
||
expect(dest[:obj]).to eq(post_reply_key)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "staged users" do
|
||
before do
|
||
SiteSetting.enable_staged_users = true
|
||
end
|
||
|
||
shared_examples "does not create staged users" do |email_name, expected_exception|
|
||
it "does not create staged users" do
|
||
staged_user_count = User.where(staged: true).count
|
||
User.expects(:create).never
|
||
User.expects(:create!).never
|
||
expect { process(email_name) }.to raise_error(expected_exception)
|
||
expect(User.where(staged: true).count).to eq(staged_user_count)
|
||
end
|
||
end
|
||
|
||
shared_examples "cleans up staged users" do |email_name, expected_exception|
|
||
it "cleans up staged users" do
|
||
staged_user_count = User.where(staged: true).count
|
||
expect { process(email_name) }.to raise_error(expected_exception)
|
||
expect(User.where(staged: true).count).to eq(staged_user_count)
|
||
end
|
||
end
|
||
|
||
context "when email address is screened" do
|
||
before do
|
||
ScreenedEmail.expects(:should_block?).with("screened@mail.com").returns(true)
|
||
end
|
||
|
||
include_examples "does not create staged users", :screened_email, Email::Receiver::ScreenedEmailError
|
||
end
|
||
|
||
context "when the mail is auto generated" do
|
||
include_examples "does not create staged users", :auto_generated_header, Email::Receiver::AutoGeneratedEmailError
|
||
end
|
||
|
||
context "when email is a bounced email" do
|
||
include_examples "does not create staged users", :bounced_email, Email::Receiver::BouncedEmailError
|
||
end
|
||
|
||
context "when the body is blank" do
|
||
include_examples "does not create staged users", :no_body, Email::Receiver::NoBodyDetectedError
|
||
end
|
||
|
||
context "when unsubscribe via email is not allowed" do
|
||
include_examples "does not create staged users", :unsubscribe_new_user, Email::Receiver::UnsubscribeNotAllowed
|
||
end
|
||
|
||
context "when From email address is not on whitelist" do
|
||
before do
|
||
SiteSetting.email_domains_whitelist = "example.com|bar.com"
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
end
|
||
|
||
include_examples "does not create staged users", :blacklist_whitelist_email, Email::Receiver::EmailNotAllowed
|
||
end
|
||
|
||
context "when From email address is on blacklist" do
|
||
before do
|
||
SiteSetting.email_domains_blacklist = "email.com|mail.com"
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
end
|
||
|
||
include_examples "does not create staged users", :blacklist_whitelist_email, Email::Receiver::EmailNotAllowed
|
||
end
|
||
|
||
context "blacklist and whitelist for To and Cc" do
|
||
before do
|
||
Fabricate(:group, incoming_email: "some_group@bar.com")
|
||
end
|
||
|
||
it "does not create staged users for email addresses not on whitelist" do
|
||
SiteSetting.email_domains_whitelist = "mail.com|example.com"
|
||
process(:blacklist_whitelist_email)
|
||
|
||
expect(User.find_by_email("alice@foo.com")).to be_nil
|
||
expect(User.find_by_email("bob@foo.com")).to be_nil
|
||
expect(User.find_by_email("carol@example.com")).to be_present
|
||
end
|
||
|
||
it "does not create staged users for email addresses on blacklist" do
|
||
SiteSetting.email_domains_blacklist = "email.com|foo.com"
|
||
process(:blacklist_whitelist_email)
|
||
|
||
expect(User.find_by_email("alice@foo.com")).to be_nil
|
||
expect(User.find_by_email("bob@foo.com")).to be_nil
|
||
expect(User.find_by_email("carol@example.com")).to be_present
|
||
end
|
||
end
|
||
|
||
context "when destinations aren't matching any of the incoming emails" do
|
||
include_examples "does not create staged users", :bad_destinations, Email::Receiver::BadDestinationAddress
|
||
end
|
||
|
||
context "when email is sent to category" do
|
||
context "when email is sent by a new user and category does not allow strangers" do
|
||
let!(:category) { Fabricate(:category, email_in: "category@foo.com", email_in_allow_strangers: false) }
|
||
|
||
include_examples "does not create staged users", :new_user, Email::Receiver::StrangersNotAllowedError
|
||
end
|
||
|
||
context "when email has no date" do
|
||
let!(:category) { Fabricate(:category, email_in: "category@foo.com", email_in_allow_strangers: true) }
|
||
|
||
it "includes the translated string in the error" do
|
||
expect { process(:no_date) }.to raise_error(Email::Receiver::InvalidPost).with_message(I18n.t("system_messages.email_reject_invalid_post_specified.date_invalid"))
|
||
end
|
||
|
||
include_examples "does not create staged users", :no_date, Email::Receiver::InvalidPost
|
||
end
|
||
end
|
||
|
||
context "email is a reply" do
|
||
let(:reply_key) { "4f97315cc828096c9cb34c6f1a0d6fe8" }
|
||
let(:category) { Fabricate(:category) }
|
||
let(:user) { Fabricate(:user, email: "discourse@bar.com") }
|
||
let!(:user2) { Fabricate(:user, email: "someone_else@bar.com") }
|
||
let(:topic) { create_topic(category: category, user: user) }
|
||
let(:post) { create_post(topic: topic, user: user) }
|
||
|
||
let!(:post_reply_key) do
|
||
Fabricate(:post_reply_key, reply_key: reply_key, user: user, post: post)
|
||
end
|
||
|
||
context "when the email address isn't matching the one we sent the notification to" do
|
||
include_examples "does not create staged users", :reply_user_not_matching, Email::Receiver::ReplyUserNotMatchingError
|
||
end
|
||
end
|
||
|
||
context "replying without key is allowed" do
|
||
let!(:group) { Fabricate(:group, incoming_email: "team@bar.com") }
|
||
let!(:topic) do
|
||
SiteSetting.find_related_post_with_key = false
|
||
process(:email_reply_1)
|
||
Topic.last
|
||
end
|
||
|
||
context "when the topic was deleted" do
|
||
before do
|
||
topic.update_columns(deleted_at: 1.day.ago)
|
||
end
|
||
|
||
include_examples "cleans up staged users", :email_reply_staged, Email::Receiver::TopicNotFoundError
|
||
end
|
||
|
||
context "when the topic was closed" do
|
||
before do
|
||
topic.update_columns(closed: true)
|
||
end
|
||
|
||
include_examples "cleans up staged users", :email_reply_staged, Email::Receiver::TopicClosedError
|
||
end
|
||
|
||
context "when they aren't allowed to like a post" do
|
||
before do
|
||
topic.update_columns(archived: true)
|
||
end
|
||
|
||
include_examples "cleans up staged users", :email_reply_like, Email::Receiver::InvalidPostAction
|
||
end
|
||
end
|
||
|
||
it "does not remove the incoming email record when staged users are deleted" do
|
||
expect { process(:bad_destinations) }.to change { IncomingEmail.count }
|
||
.and raise_error(Email::Receiver::BadDestinationAddress)
|
||
expect(IncomingEmail.last.message_id).to eq("9@foo.bar.mail")
|
||
end
|
||
end
|
||
|
||
context "mailing list mirror" do
|
||
let!(:category) { Fabricate(:mailinglist_mirror_category) }
|
||
|
||
before do
|
||
SiteSetting.block_auto_generated_emails = true
|
||
end
|
||
|
||
it "should allow creating topic even when email is autogenerated" do
|
||
expect { process(:mailinglist) }.to change { Topic.count }
|
||
expect(IncomingEmail.last.is_auto_generated).to eq(false)
|
||
end
|
||
|
||
it "should allow replying without reply key" do
|
||
process(:mailinglist)
|
||
topic = Topic.last
|
||
|
||
expect { process(:mailinglist_reply) }.to change { topic.posts.count }
|
||
end
|
||
|
||
it "should skip validations for staged users" do
|
||
Fabricate(:user, email: "alice@foo.com", staged: true)
|
||
expect { process(:mailinglist_short_message) }.to change { Topic.count }
|
||
end
|
||
|
||
it "should skip validations for regular users" do
|
||
Fabricate(:user, email: "alice@foo.com")
|
||
expect { process(:mailinglist_short_message) }.to change { Topic.count }
|
||
end
|
||
|
||
context "read-only category" do
|
||
before do
|
||
category.set_permissions(everyone: :readonly)
|
||
category.save
|
||
|
||
Fabricate(:user, email: "alice@foo.com")
|
||
Fabricate(:user, email: "bob@bar.com")
|
||
end
|
||
|
||
it "should allow creating topic within read-only category" do
|
||
expect { process(:mailinglist) }.to change { Topic.count }
|
||
end
|
||
|
||
it "should allow replying within read-only category" do
|
||
process(:mailinglist)
|
||
topic = Topic.last
|
||
|
||
expect { process(:mailinglist_reply) }.to change { topic.posts.count }
|
||
end
|
||
end
|
||
|
||
it "ignores unsubscribe email" do
|
||
SiteSetting.unsubscribe_via_email = true
|
||
Fabricate(:user, email: "alice@foo.com")
|
||
|
||
expect { process("mailinglist_unsubscribe") }.to_not change { ActionMailer::Base.deliveries.count }
|
||
end
|
||
end
|
||
|
||
it "tries to fix unparsable email addresses in To and CC headers" do
|
||
expect { process(:unparsable_email_addresses) }.to raise_error(Email::Receiver::BadDestinationAddress)
|
||
|
||
email = IncomingEmail.last
|
||
expect(email.to_addresses).to eq("foo@bar.com")
|
||
expect(email.cc_addresses).to eq("bob@example.com;carol@example.com")
|
||
end
|
||
end
|