2013-11-25 11:29:37 -05:00
|
|
|
# -*- encoding : utf-8 -*-
|
|
|
|
|
2013-06-10 16:46:08 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
require 'email/receiver'
|
|
|
|
|
|
|
|
describe Email::Receiver do
|
|
|
|
|
2013-06-13 18:11:10 -04:00
|
|
|
before do
|
2014-07-31 04:46:02 -04:00
|
|
|
SiteSetting.reply_by_email_address = "reply+%{reply_key}@appmail.adventuretime.ooo"
|
|
|
|
SiteSetting.email_in = false
|
2014-08-26 20:08:53 -04:00
|
|
|
SiteSetting.title = "Discourse"
|
2013-06-13 18:11:10 -04:00
|
|
|
end
|
2013-06-10 16:46:08 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe 'parse_body' do
|
|
|
|
def test_parse_body(mail_string)
|
|
|
|
Email::Receiver.new(nil).parse_body(Mail::Message.new mail_string)
|
|
|
|
end
|
|
|
|
|
2014-02-28 07:05:09 -05:00
|
|
|
it "raises EmptyEmailError if the message is blank" do
|
2014-08-26 20:08:53 -04:00
|
|
|
expect { test_parse_body("") }.to raise_error(Email::Receiver::EmptyEmailError)
|
2013-06-13 18:11:10 -04:00
|
|
|
end
|
2013-06-10 16:46:08 -04:00
|
|
|
|
2014-07-11 15:26:40 -04:00
|
|
|
it "raises EmptyEmailError if the message is not an email" do
|
2014-08-26 20:08:53 -04:00
|
|
|
expect { test_parse_body("asdf" * 30) }.to raise_error(Email::Receiver::EmptyEmailError)
|
2013-06-10 16:46:08 -04:00
|
|
|
end
|
2014-07-11 15:26:40 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "raises EmptyEmailError if there is no reply content" do
|
|
|
|
expect { test_parse_body(fixture_file("emails/no_content_reply.eml")) }.to raise_error(Email::Receiver::EmptyEmailError)
|
2014-07-11 15:26:40 -04:00
|
|
|
end
|
2013-06-13 18:11:10 -04:00
|
|
|
|
2014-09-25 11:44:48 -04:00
|
|
|
skip "raises EmailUnparsableError if the headers are corrupted" do
|
2014-08-26 20:08:53 -04:00
|
|
|
expect { ; }.to raise_error(Email::Receiver::EmailUnparsableError)
|
|
|
|
end
|
2013-06-20 12:38:03 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "can parse the html section" do
|
|
|
|
test_parse_body(fixture_file("emails/html_only.eml")).should == "The EC2 instance - I've seen that there tends to be odd and " +
|
|
|
|
"unrecommended settings on the Bitnami installs that I've checked out."
|
2013-06-20 12:38:03 -04:00
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "supports a Dutch reply" do
|
|
|
|
test_parse_body(fixture_file("emails/dutch.eml")).should == "Dit is een antwoord in het Nederlands."
|
|
|
|
end
|
|
|
|
|
|
|
|
it "supports a Hebrew reply" do
|
|
|
|
I18n.expects(:t).with('user_notifications.previous_discussion').returns('כלטוב')
|
2013-06-20 12:38:03 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
# The force_encoding call is only needed for the test - it is passed on fine to the cooked post
|
2014-08-28 15:09:42 -04:00
|
|
|
test_parse_body(fixture_file("emails/hebrew.eml")).should == "שלום"
|
2013-06-20 12:38:03 -04:00
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "supports a BIG5-encoded reply" do
|
|
|
|
I18n.expects(:t).with('user_notifications.previous_discussion').returns('媽!我上電視了!')
|
2013-07-24 14:22:32 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
# The force_encoding call is only needed for the test - it is passed on fine to the cooked post
|
2014-08-28 15:09:42 -04:00
|
|
|
test_parse_body(fixture_file("emails/big5.eml")).should == "媽!我上電視了!"
|
2013-07-24 14:22:32 -04:00
|
|
|
end
|
2013-08-21 16:54:01 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "removes 'via' lines if they match the site title" do
|
|
|
|
SiteSetting.title = "Discourse"
|
2013-11-04 15:30:24 -05:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
test_parse_body(fixture_file("emails/via_line.eml")).should == "Hello this email has content!"
|
2013-11-04 15:30:24 -05:00
|
|
|
end
|
|
|
|
|
2014-09-09 19:24:20 -04:00
|
|
|
it "removes an 'on date wrote' quoting line" do
|
|
|
|
test_parse_body(fixture_file("emails/on_wrote.eml")).should == "Sure, all you need to do is frobnicate the foobar and you'll be all set!"
|
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "removes the 'Previous Discussion' marker" do
|
|
|
|
test_parse_body(fixture_file("emails/previous.eml")).should == "This will not include the previous discussion that is present in this email."
|
|
|
|
end
|
2014-01-16 21:24:32 -05:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "handles multiple paragraphs" do
|
|
|
|
test_parse_body(fixture_file("emails/paragraphs.eml")).
|
|
|
|
should == (
|
|
|
|
"Is there any reason the *old* candy can't be be kept in silos while the new candy
|
|
|
|
is imported into *new* silos?
|
|
|
|
|
|
|
|
The thing about candy is it stays delicious for a long time -- we can just keep
|
|
|
|
it there without worrying about it too much, imo.
|
|
|
|
|
|
|
|
Thanks for listening."
|
|
|
|
)
|
2014-01-16 21:24:32 -05:00
|
|
|
end
|
2014-08-28 15:09:42 -04:00
|
|
|
|
2014-10-08 10:15:46 -04:00
|
|
|
it "handles multiple paragraphs when parsing html" do
|
|
|
|
test_parse_body(fixture_file("emails/html_paragraphs.eml")).
|
|
|
|
should == (
|
|
|
|
"Awesome!
|
|
|
|
|
|
|
|
Pleasure to have you here!
|
|
|
|
|
|
|
|
:boom:"
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2014-10-14 06:12:01 -04:00
|
|
|
it "properly renders email reply from MS Outlook client" do
|
|
|
|
test_parse_body(fixture_file("emails/outlook.eml")).should == "Microsoft Outlook 2010"
|
|
|
|
end
|
|
|
|
|
2014-08-28 15:09:42 -04:00
|
|
|
it "converts back to UTF-8 at the end" do
|
|
|
|
result = test_parse_body(fixture_file("emails/big5.eml"))
|
|
|
|
result.encoding.should == Encoding::UTF_8
|
|
|
|
|
|
|
|
# should not throw
|
|
|
|
TextCleaner.normalize_whitespaces(
|
|
|
|
test_parse_body(fixture_file("emails/big5.eml"))
|
|
|
|
)
|
|
|
|
end
|
2014-01-16 21:24:32 -05:00
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe "posting replies" do
|
|
|
|
let(:reply_key) { raise "Override this in a lower describe block" }
|
|
|
|
let(:email_raw) { raise "Override this in a lower describe block" }
|
|
|
|
# ----
|
|
|
|
let(:receiver) { Email::Receiver.new(email_raw) }
|
|
|
|
let(:post) { create_post }
|
|
|
|
let(:topic) { post.topic }
|
|
|
|
let(:posting_user) { post.user }
|
|
|
|
let(:replying_user_email) { 'jake@adventuretime.ooo' }
|
|
|
|
let(:replying_user) { Fabricate(:user, email: replying_user_email, trust_level: 2)}
|
|
|
|
let(:email_log) { EmailLog.new(reply_key: reply_key,
|
|
|
|
post: post,
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic_id,
|
|
|
|
email_type: 'user_posted',
|
|
|
|
user: replying_user,
|
|
|
|
user_id: replying_user.id,
|
|
|
|
to_address: replying_user_email
|
|
|
|
) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
email_log.save
|
|
|
|
end
|
|
|
|
|
|
|
|
# === Success Posting ===
|
|
|
|
|
|
|
|
describe "valid_reply.eml" do
|
|
|
|
let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
|
|
|
|
let!(:email_raw) { fixture_file("emails/valid_reply.eml") }
|
|
|
|
|
|
|
|
it "creates a post with the correct content" do
|
|
|
|
start_count = topic.posts.count
|
|
|
|
|
|
|
|
receiver.process
|
|
|
|
|
|
|
|
topic.posts.count.should == (start_count + 1)
|
2014-09-04 13:04:22 -04:00
|
|
|
created_post = topic.posts.last
|
2014-09-25 11:44:48 -04:00
|
|
|
created_post.via_email.should == true
|
2014-10-16 02:08:02 -04:00
|
|
|
created_post.raw_email.should == fixture_file("emails/valid_reply.eml")
|
2014-09-04 13:04:22 -04:00
|
|
|
created_post.cooked.strip.should == fixture_file("emails/valid_reply.cooked").strip
|
2014-08-26 20:08:53 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "paragraphs.eml" do
|
|
|
|
let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
|
|
|
|
let!(:email_raw) { fixture_file("emails/paragraphs.eml") }
|
2013-08-21 16:54:01 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "cooks multiple paragraphs with traditional Markdown linebreaks" do
|
|
|
|
start_count = topic.posts.count
|
|
|
|
|
|
|
|
receiver.process
|
|
|
|
|
|
|
|
topic.posts.count.should == (start_count + 1)
|
|
|
|
topic.posts.last.cooked.strip.should == fixture_file("emails/paragraphs.cooked").strip
|
|
|
|
topic.posts.last.cooked.should_not match /<br/
|
|
|
|
end
|
2013-08-21 16:54:01 -04:00
|
|
|
end
|
2013-07-24 14:22:32 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe "attachment.eml" do
|
|
|
|
let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
|
|
|
|
let!(:email_raw) {
|
|
|
|
fixture_file("emails/attachment.eml")
|
|
|
|
.gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo")
|
|
|
|
.gsub("FROM", replying_user_email)
|
|
|
|
}
|
|
|
|
|
|
|
|
let(:upload_sha) { '04df605be528d03876685c52166d4b063aabb78a' }
|
|
|
|
|
|
|
|
it "creates a post with an attachment" do
|
|
|
|
start_count = topic.posts.count
|
|
|
|
Upload.find_by(sha1: upload_sha).try :destroy
|
|
|
|
|
|
|
|
receiver.process
|
|
|
|
|
|
|
|
topic.posts.count.should == (start_count + 1)
|
|
|
|
topic.posts.last.cooked.should match /<img src=['"](\/uploads\/default\/\d+\/\w{16}\.png)['"] width=['"]289['"] height=['"]126['"]>/
|
2014-09-25 11:44:48 -04:00
|
|
|
Upload.find_by(sha1: upload_sha).should_not == nil
|
2014-08-26 20:08:53 -04:00
|
|
|
end
|
2013-07-24 14:22:32 -04:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
# === Failure Conditions ===
|
2013-07-24 14:22:32 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe "too_short.eml" do
|
|
|
|
let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
|
|
|
|
let!(:email_raw) {
|
|
|
|
fixture_file("emails/too_short.eml")
|
|
|
|
.gsub("TO", "reply+#{reply_key}@appmail.adventuretime.ooo")
|
|
|
|
.gsub("FROM", replying_user_email)
|
|
|
|
.gsub("SUBJECT", "re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'")
|
|
|
|
}
|
|
|
|
|
|
|
|
it "raises an InvalidPost error" do
|
|
|
|
SiteSetting.min_post_length = 5
|
|
|
|
expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost)
|
|
|
|
end
|
2013-07-24 14:22:32 -04:00
|
|
|
end
|
2014-08-26 20:08:53 -04:00
|
|
|
|
|
|
|
describe "too_many_mentions.eml" do
|
|
|
|
let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
|
|
|
|
let!(:email_raw) { fixture_file("emails/too_many_mentions.eml") }
|
|
|
|
|
|
|
|
it "raises an InvalidPost error" do
|
|
|
|
SiteSetting.max_mentions_per_post = 10
|
|
|
|
(1..11).each do |i|
|
|
|
|
Fabricate(:user, username: "user#{i}").save
|
|
|
|
end
|
|
|
|
|
|
|
|
expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-07-24 14:22:32 -04:00
|
|
|
end
|
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe "posting a new topic" do
|
|
|
|
let(:category_destination) { raise "Override this in a lower describe block" }
|
|
|
|
let(:email_raw) { raise "Override this in a lower describe block" }
|
|
|
|
let(:allow_strangers) { false }
|
|
|
|
# ----
|
|
|
|
let(:receiver) { Email::Receiver.new(email_raw) }
|
|
|
|
let(:user_email) { 'jake@adventuretime.ooo' }
|
|
|
|
let(:user) { Fabricate(:user, email: user_email, trust_level: 2)}
|
|
|
|
let(:category) { Fabricate(:category, email_in: category_destination, email_in_allow_strangers: allow_strangers) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
SiteSetting.email_in = true
|
|
|
|
user.save
|
|
|
|
category.save
|
|
|
|
end
|
2013-06-25 14:05:14 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
describe "too_short.eml" do
|
|
|
|
let!(:category_destination) { 'incoming+amazing@appmail.adventuretime.ooo' }
|
|
|
|
let(:email_raw) {
|
|
|
|
fixture_file("emails/too_short.eml")
|
|
|
|
.gsub("TO", category_destination)
|
|
|
|
.gsub("FROM", user_email)
|
|
|
|
.gsub("SUBJECT", "A long subject that passes the checks")
|
|
|
|
}
|
2013-06-25 14:05:14 -04:00
|
|
|
|
2014-08-26 20:08:53 -04:00
|
|
|
it "does not create a topic if the post fails" do
|
|
|
|
before_topic_count = Topic.count
|
|
|
|
|
|
|
|
expect { receiver.process }.to raise_error(Email::Receiver::InvalidPost)
|
|
|
|
|
|
|
|
Topic.count.should == before_topic_count
|
|
|
|
end
|
2013-06-25 14:05:14 -04:00
|
|
|
|
|
|
|
end
|
2014-08-26 20:08:53 -04:00
|
|
|
|
2013-06-25 14:05:14 -04:00
|
|
|
end
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
def fill_email(mail, from, to, body = nil, subject = nil)
|
|
|
|
result = mail.gsub("FROM", from).gsub("TO", to)
|
|
|
|
if body
|
|
|
|
result.gsub!(/Hey.*/m, body)
|
|
|
|
end
|
|
|
|
if subject
|
|
|
|
result.sub!(/We .*/, subject)
|
|
|
|
end
|
|
|
|
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::Receiver.new(email).process
|
|
|
|
end
|
|
|
|
|
2013-06-13 18:11:10 -04:00
|
|
|
describe "with a valid email" do
|
|
|
|
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
|
2014-07-31 04:46:02 -04:00
|
|
|
let(:to) { SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key) }
|
|
|
|
|
|
|
|
let(:valid_reply) {
|
|
|
|
reply = fixture_file("emails/valid_reply.eml")
|
|
|
|
to = SiteSetting.reply_by_email_address.gsub("%{reply_key}", reply_key)
|
|
|
|
fill_email(reply, "test@test.com", to)
|
|
|
|
}
|
|
|
|
|
2013-06-13 18:11:10 -04:00
|
|
|
let(:receiver) { Email::Receiver.new(valid_reply) }
|
2014-07-31 04:46:02 -04:00
|
|
|
let(:post) { create_post }
|
|
|
|
let(:user) { post.user }
|
|
|
|
let(:email_log) { EmailLog.new(reply_key: reply_key,
|
|
|
|
post_id: post.id,
|
|
|
|
topic_id: post.topic_id,
|
|
|
|
user_id: post.user_id,
|
|
|
|
post: post,
|
|
|
|
user: user,
|
|
|
|
email_type: 'test',
|
|
|
|
to_address: 'test@test.com'
|
|
|
|
) }
|
2013-06-13 18:11:10 -04:00
|
|
|
let(:reply_body) {
|
|
|
|
"I could not disagree more. I am obviously biased but adventure time is the
|
|
|
|
greatest show ever created. Everyone should watch it.
|
|
|
|
|
|
|
|
- Jake out" }
|
|
|
|
|
|
|
|
describe "with an email log" do
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
it "extracts data" do
|
|
|
|
expect{ receiver.process }.to raise_error(Email::Receiver::EmailLogNotFound)
|
2013-06-13 18:11:10 -04:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
email_log.save!
|
|
|
|
receiver.process
|
2013-06-13 18:11:10 -04:00
|
|
|
|
|
|
|
expect(receiver.body).to eq(reply_body)
|
|
|
|
expect(receiver.email_log).to eq(email_log)
|
2014-03-28 09:57:12 -04:00
|
|
|
end
|
2014-04-14 16:55:57 -04:00
|
|
|
|
2014-03-28 09:57:12 -04:00
|
|
|
end
|
|
|
|
|
2013-06-10 16:46:08 -04:00
|
|
|
end
|
|
|
|
|
2014-02-27 07:44:21 -05:00
|
|
|
describe "processes an email to a category" do
|
|
|
|
before do
|
2014-07-31 04:46:02 -04:00
|
|
|
SiteSetting.email_in = true
|
2014-02-27 07:44:21 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
it "correctly can target categories" do
|
|
|
|
to = "some@email.com"
|
2014-02-27 07:44:21 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
Fabricate(:category, email_in_allow_strangers: false, email_in: to)
|
2014-09-05 01:20:39 -04:00
|
|
|
SiteSetting.email_in_min_trust = TrustLevel[4].to_s
|
2014-02-27 07:44:21 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# no email in for user
|
|
|
|
expect{
|
|
|
|
process_email(from: "cobb@dob.com", to: "invalid@address.com")
|
2014-08-13 14:06:17 -04:00
|
|
|
}.to raise_error(Email::Receiver::BadDestinationAddress)
|
2014-02-27 07:44:21 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# valid target invalid user
|
|
|
|
expect{
|
|
|
|
process_email(from: "cobb@dob.com", to: to)
|
|
|
|
}.to raise_error(Email::Receiver::UserNotFoundError)
|
2014-02-27 07:44:21 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# untrusted
|
|
|
|
user = Fabricate(:user)
|
|
|
|
expect{
|
|
|
|
process_email(from: user.email, to: to)
|
|
|
|
}.to raise_error(Email::Receiver::UserNotSufficientTrustLevelError)
|
2014-02-27 07:44:21 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# trusted
|
|
|
|
user.trust_level = 4
|
|
|
|
user.save
|
2014-02-24 11:36:53 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
process_email(from: user.email, to: to)
|
|
|
|
user.posts.count.should == 1
|
2014-02-24 11:36:53 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# email too short
|
|
|
|
message = nil
|
|
|
|
begin
|
|
|
|
process_email(from: user.email, to: to, body: "x", subject: "this is my new topic title")
|
|
|
|
rescue Email::Receiver::InvalidPost => e
|
|
|
|
message = e.message
|
2014-02-24 11:36:53 -05:00
|
|
|
end
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
e.message.should include("too short")
|
2014-02-24 11:36:53 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2013-06-13 18:11:10 -04:00
|
|
|
|
2014-02-27 10:36:33 -05:00
|
|
|
|
|
|
|
describe "processes an unknown email sender to category" do
|
|
|
|
before do
|
2014-07-31 04:46:02 -04:00
|
|
|
SiteSetting.email_in = true
|
2014-02-27 10:36:33 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
it "rejects anon email" do
|
|
|
|
Fabricate(:category, email_in_allow_strangers: false, email_in: "bob@bob.com")
|
|
|
|
expect { process_email(from: "test@test.com", to: "bob@bob.com") }.to raise_error(Email::Receiver::UserNotFoundError)
|
2014-02-27 10:36:33 -05:00
|
|
|
end
|
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
it "creates a topic for allowed category" do
|
|
|
|
Fabricate(:category, email_in_allow_strangers: true, email_in: "bob@bob.com")
|
|
|
|
process_email(from: "test@test.com", to: "bob@bob.com")
|
2014-02-27 10:36:33 -05:00
|
|
|
|
2014-07-31 04:46:02 -04:00
|
|
|
# This is the current implementation but it is wrong, it should register an account
|
|
|
|
Discourse.system_user.posts.order("id desc").limit(1).pluck(:raw).first.should include("Hey folks")
|
2014-02-27 10:36:33 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-06-10 16:46:08 -04:00
|
|
|
end
|