Commit e9972efc authored by Douwe Maan's avatar Douwe Maan

Extract ReplyParser and AttachmentUploader from Receiver.

parent 3ff9d5c6
...@@ -7,7 +7,7 @@ class EmailReceiverWorker ...@@ -7,7 +7,7 @@ class EmailReceiverWorker
return unless Gitlab::ReplyByEmail.enabled? return unless Gitlab::ReplyByEmail.enabled?
begin begin
Gitlab::EmailReceiver.new(raw).execute Gitlab::Email::Receiver.new(raw).execute
rescue => e rescue => e
handle_failure(raw, e) handle_failure(raw, e)
end end
...@@ -22,20 +22,20 @@ class EmailReceiverWorker ...@@ -22,20 +22,20 @@ class EmailReceiverWorker
reason = nil reason = nil
case e case e
when Gitlab::EmailReceiver::SentNotificationNotFound when Gitlab::Email::Receiver::SentNotificationNotFound
reason = "We couldn't figure out what the email is in reply to. Please create your comment through the web interface." reason = "We couldn't figure out what the email is in reply to. Please create your comment through the web interface."
when Gitlab::EmailReceiver::EmptyEmailError when Gitlab::Email::Receiver::EmptyEmailError
can_retry = true can_retry = true
reason = "It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies." reason = "It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
when Gitlab::EmailReceiver::AutoGeneratedEmailError when Gitlab::Email::Receiver::AutoGeneratedEmailError
reason = "The email was marked as 'auto generated', which we can't accept. Please create your comment through the web interface." reason = "The email was marked as 'auto generated', which we can't accept. Please create your comment through the web interface."
when Gitlab::EmailReceiver::UserNotFoundError when Gitlab::Email::Receiver::UserNotFoundError
reason = "We couldn't figure out what user corresponds to the email. Please create your comment through the web interface." reason = "We couldn't figure out what user corresponds to the email. Please create your comment through the web interface."
when Gitlab::EmailReceiver::UserNotAuthorizedError when Gitlab::Email::Receiver::UserNotAuthorizedError
reason = "You are not allowed to respond to the thread you are replying to. If you believe this is in error, contact a staff member." reason = "You are not allowed to respond to the thread you are replying to. If you believe this is in error, contact a staff member."
when Gitlab::EmailReceiver::NoteableNotFoundError when Gitlab::Email::Receiver::NoteableNotFoundError
reason = "The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member." reason = "The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member."
when Gitlab::EmailReceiver::InvalidNote when Gitlab::Email::Receiver::InvalidNote
can_retry = true can_retry = true
reason = e.message reason = e.message
else else
......
module Gitlab
module Email
module AttachmentUploader
attr_accessor :message
def initialize(message)
@message = message
end
def execute(project)
attachments = []
message.attachments.each do |attachment|
tmp = Tempfile.new("gitlab-email-attachment")
begin
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
file = {
tempfile: tmp,
filename: attachment.filename,
content_type: attachment.content_type
}
link = ::Projects::UploadService.new(project, file).execute
attachments << link if link
ensure
tmp.close!
end
end
attachments
end
end
end
end
# Taken mostly from Discourse's Email::HtmlCleaner
module Gitlab
module Email
# HtmlCleaner cleans up the extremely dirty HTML that many email clients
# generate by stripping out any excess divs or spans, removing styling in
# the process (which also makes the html more suitable to be parsed as
# Markdown).
class HtmlCleaner
# Elements to hoist all children out of
HTML_HOIST_ELEMENTS = %w(div span font table tbody th tr td)
# Node types to always delete
HTML_DELETE_ELEMENT_TYPES = [
Nokogiri::XML::Node::DTD_NODE,
Nokogiri::XML::Node::COMMENT_NODE,
]
# Private variables:
# @doc - nokogiri document
# @out - same as @doc, but only if trimming has occured
def initialize(html)
if html.is_a?(String)
@doc = Nokogiri::HTML(html)
else
@doc = html
end
end
class << self
# HtmlCleaner.trim(inp, opts={})
#
# Arguments:
# inp - Either a HTML string or a Nokogiri document.
# Options:
# :return => :doc, :string
# Specify the desired return type.
# Defaults to the type of the input.
# A value of :string is equivalent to calling get_document_text()
# on the returned document.
def trim(inp, opts={})
cleaner = HtmlCleaner.new(inp)
opts[:return] ||= (inp.is_a?(String) ? :string : :doc)
if opts[:return] == :string
cleaner.output_html
else
cleaner.output_document
end
end
# HtmlCleaner.get_document_text(doc)
#
# Get the body portion of the document, including html, as a string.
def get_document_text(doc)
body = doc.xpath('//body')
if body
body.inner_html
else
doc.inner_html
end
end
end
def output_document
@out ||= begin
doc = @doc
trim_process_node doc
add_newlines doc
doc
end
end
def output_html
HtmlCleaner.get_document_text(output_document)
end
private
def add_newlines(doc)
# Replace <br> tags with a markdown \n
doc.xpath('//br').each do |br|
br.replace(new_linebreak_node doc, 2)
end
# Surround <p> tags with newlines, to help with line-wise postprocessing
# and ensure markdown paragraphs
doc.xpath('//p').each do |p|
p.before(new_linebreak_node doc)
p.after(new_linebreak_node doc, 2)
end
end
def new_linebreak_node(doc, count=1)
Nokogiri::XML::Text.new("\n" * count, doc)
end
def trim_process_node(node)
if should_hoist?(node)
hoisted = trim_hoist_element node
hoisted.each { |child| trim_process_node child }
elsif should_delete?(node)
node.remove
else
if children = node.children
children.each { |child| trim_process_node child }
end
end
node
end
def trim_hoist_element(element)
hoisted = []
element.children.each do |child|
element.before(child)
hoisted << child
end
element.remove
hoisted
end
def should_hoist?(node)
return false unless node.element?
HTML_HOIST_ELEMENTS.include? node.name
end
def should_delete?(node)
return true if HTML_DELETE_ELEMENT_TYPES.include? node.type
return true if node.element? && node.name == 'head'
return true if node.text? && node.text.strip.blank?
false
end
end
end
end
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
class Receiver
class ProcessingError < StandardError; end
class EmailUnparsableError < ProcessingError; end
class EmptyEmailError < ProcessingError; end
class UserNotFoundError < ProcessingError; end
class UserNotAuthorizedError < ProcessingError; end
class NoteableNotFoundError < ProcessingError; end
class AutoGeneratedEmailError < ProcessingError; end
class SentNotificationNotFound < ProcessingError; end
class InvalidNote < ProcessingError; end
def initialize(raw)
@raw = raw
end
def message
@message ||= Mail::Message.new(@raw)
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
raise EmailUnparsableError, e
end
def execute
raise SentNotificationNotFound unless sent_notification
raise EmptyEmailError if @raw.blank?
raise AutoGeneratedEmailError if message.header.to_s =~ /auto-(generated|replied)/
author = sent_notification.recipient
raise UserNotFoundError unless author
project = sent_notification.project
raise UserNotAuthorizedError unless author.can?(:create_note, project)
raise NoteableNotFoundError unless sent_notification.noteable
reply = ReplyParser.new(message).execute.strip
raise EmptyEmailError if reply.blank?
reply = add_attachments(reply)
note = create_note(reply)
unless note.persisted?
message = "The comment could not be created for the following reasons:"
note.errors.full_messages.each do |error|
message << "\n\n- #{error}"
end
raise InvalidNote, message
end
end
private
def reply_key
reply_key = nil
message.to.each do |address|
reply_key = Gitlab::ReplyByEmail.reply_key_from_address(address)
break if reply_key
end
reply_key
end
def sent_notification
return nil unless reply_key
SentNotification.for(reply_key)
end
def add_attachments(reply)
attachments = AttachmentUploader.new(message).execute(project)
attachments.each do |link|
text = "[#{link[:alt]}](#{link[:url]})"
text.prepend("!") if link[:is_image]
reply << "\n\n#{text}"
end
end
def create_note(reply)
Notes::CreateService.new(
sent_notification.project,
sent_notification.recipient,
note: reply,
noteable_type: sent_notification.noteable_type,
noteable_id: sent_notification.noteable_id,
commit_id: sent_notification.commit_id
).execute
end
end
end
end
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
class ReplyParser
attr_accessor :message
def initialize(message)
@message = message
end
def execute
body = select_body(message)
encoding = body.encoding
body = discourse_email_trimmer(body)
body = EmailReplyParser.parse_reply(body)
body.force_encoding(encoding).encode("UTF-8")
end
private
def select_body(message)
html = nil
text = nil
if message.multipart?
html = fix_charset(message.html_part)
text = fix_charset(message.text_part)
elsif message.content_type =~ /text\/html/
html = fix_charset(message)
end
# prefer plain text
return text if text
if html
body = HtmlCleaner.new(html).output_html
else
body = fix_charset(message)
end
# Certain trigger phrases that means we didn't parse correctly
if body =~ /(Content\-Type\:|multipart\/alternative|text\/plain)/
return ""
end
body
end
# Force encoding to UTF-8 on a Mail::Message or Mail::Part
def fix_charset(object)
return nil if object.nil?
if object.charset
object.body.decoded.force_encoding(object.charset.gsub(/utf8/i, "UTF-8")).encode("UTF-8").to_s
else
object.body.to_s
end
rescue
nil
end
REPLYING_HEADER_LABELS = %w(From Sent To Subject Reply To Cc Bcc Date)
REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |label| "#{label}:" })
def discourse_email_trimmer(body)
lines = body.scrub.lines.to_a
range_end = 0
lines.each_with_index do |l, idx|
# This one might be controversial but so many reply lines have years, times and end with a colon.
# Let's try it and see how well it works.
break if (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) ||
(l =~ /On \w+ \d+,? \d+,?.*wrote:/)
# Headers on subsequent lines
break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX }
# Headers on the same line
break if REPLYING_HEADER_LABELS.count { |label| l.include?(label) } >= 3
range_end = idx
end
lines[0..range_end].join.strip
end
end
end
end
# Taken mostly from Discourse's Email::HtmlCleaner
module Gitlab
# HtmlCleaner cleans up the extremely dirty HTML that many email clients
# generate by stripping out any excess divs or spans, removing styling in
# the process (which also makes the html more suitable to be parsed as
# Markdown).
class EmailHtmlCleaner
# Elements to hoist all children out of
HTML_HOIST_ELEMENTS = %w(div span font table tbody th tr td)
# Node types to always delete
HTML_DELETE_ELEMENT_TYPES = [
Nokogiri::XML::Node::DTD_NODE,
Nokogiri::XML::Node::COMMENT_NODE,
]
# Private variables:
# @doc - nokogiri document
# @out - same as @doc, but only if trimming has occured
def initialize(html)
if html.is_a?(String)
@doc = Nokogiri::HTML(html)
else
@doc = html
end
end
class << self
# EmailHtmlCleaner.trim(inp, opts={})
#
# Arguments:
# inp - Either a HTML string or a Nokogiri document.
# Options:
# :return => :doc, :string
# Specify the desired return type.
# Defaults to the type of the input.
# A value of :string is equivalent to calling get_document_text()
# on the returned document.
def trim(inp, opts={})
cleaner = EmailHtmlCleaner.new(inp)
opts[:return] ||= (inp.is_a?(String) ? :string : :doc)
if opts[:return] == :string
cleaner.output_html
else
cleaner.output_document
end
end
# EmailHtmlCleaner.get_document_text(doc)
#
# Get the body portion of the document, including html, as a string.
def get_document_text(doc)
body = doc.xpath('//body')
if body
body.inner_html
else
doc.inner_html
end
end
end
def output_document
@out ||= begin
doc = @doc
trim_process_node doc
add_newlines doc
doc
end
end
def output_html
EmailHtmlCleaner.get_document_text(output_document)
end
private
def add_newlines(doc)
# Replace <br> tags with a markdown \n
doc.xpath('//br').each do |br|
br.replace(new_linebreak_node doc, 2)
end
# Surround <p> tags with newlines, to help with line-wise postprocessing
# and ensure markdown paragraphs
doc.xpath('//p').each do |p|
p.before(new_linebreak_node doc)
p.after(new_linebreak_node doc, 2)
end
end
def new_linebreak_node(doc, count=1)
Nokogiri::XML::Text.new("\n" * count, doc)
end
def trim_process_node(node)
if should_hoist?(node)
hoisted = trim_hoist_element node
hoisted.each { |child| trim_process_node child }
elsif should_delete?(node)
node.remove
else
if children = node.children
children.each { |child| trim_process_node child }
end
end
node
end
def trim_hoist_element(element)
hoisted = []
element.children.each do |child|
element.before(child)
hoisted << child
end
element.remove
hoisted
end
def should_hoist?(node)
return false unless node.element?
HTML_HOIST_ELEMENTS.include? node.name
end
def should_delete?(node)
return true if HTML_DELETE_ELEMENT_TYPES.include? node.type
return true if node.element? && node.name == 'head'
return true if node.text? && node.text.strip.blank?
false
end
end
end
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
class EmailReceiver
class ProcessingError < StandardError; end
class EmailUnparsableError < ProcessingError; end
class EmptyEmailError < ProcessingError; end
class UserNotFoundError < ProcessingError; end
class UserNotAuthorizedError < ProcessingError; end
class NoteableNotFoundError < ProcessingError; end
class AutoGeneratedEmailError < ProcessingError; end
class SentNotificationNotFound < ProcessingError; end
class InvalidNote < ProcessingError; end
def initialize(raw)
@raw = raw
end
def message
@message ||= Mail::Message.new(@raw)
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
raise EmailUnparsableError, e
end
def execute
raise SentNotificationNotFound unless sent_notification
raise EmptyEmailError if @raw.blank?
raise AutoGeneratedEmailError if message.header.to_s =~ /auto-(generated|replied)/
author = sent_notification.recipient
raise UserNotFoundError unless author
project = sent_notification.project
raise UserNotAuthorizedError unless author.can?(:create_note, project)
raise NoteableNotFoundError unless sent_notification.noteable
body = parse_body(message)
upload_attachments.each do |link|
body << "\n\n#{link}"
end
note = Notes::CreateService.new(
project,
author,
note: body,
noteable_type: sent_notification.noteable_type,
noteable_id: sent_notification.noteable_id,
commit_id: sent_notification.commit_id
).execute
unless note.persisted?
message = "The comment could not be created for the following reasons:"
note.errors.full_messages.each do |error|
message << "\n\n- #{error}"
end
raise InvalidNote, message
end
end
def parse_body(message)
body = select_body(message)
encoding = body.encoding
raise EmptyEmailError if body.strip.blank?
body = discourse_email_trimmer(body)
raise EmptyEmailError if body.strip.blank?
body = EmailReplyParser.parse_reply(body)
raise EmptyEmailError if body.strip.blank?
body.force_encoding(encoding).encode("UTF-8")
end
private
def reply_key
reply_key = nil
message.to.each do |address|
reply_key = Gitlab::ReplyByEmail.reply_key_from_address(address)
break if reply_key
end
reply_key
end
def sent_notification
return nil unless reply_key
SentNotification.for(reply_key)
end
def select_body(message)
html = nil
text = nil
if message.multipart?
html = fix_charset(message.html_part)
text = fix_charset(message.text_part)
elsif message.content_type =~ /text\/html/
html = fix_charset(message)
end
# prefer plain text
return text if text
if html
body = EmailHtmlCleaner.new(html).output_html
else
body = fix_charset(message)
end
# Certain trigger phrases that means we didn't parse correctly
if body =~ /(Content\-Type\:|multipart\/alternative|text\/plain)/
raise EmptyEmailError
end
body
end
# Force encoding to UTF-8 on a Mail::Message or Mail::Part
def fix_charset(object)
return nil if object.nil?
if object.charset
object.body.decoded.force_encoding(object.charset.gsub(/utf8/i, "UTF-8")).encode("UTF-8").to_s
else
object.body.to_s
end
rescue
nil
end
REPLYING_HEADER_LABELS = %w(From Sent To Subject Reply To Cc Bcc Date)
REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |label| "#{label}:" })
def discourse_email_trimmer(body)
lines = body.scrub.lines.to_a
range_end = 0
lines.each_with_index do |l, idx|
# This one might be controversial but so many reply lines have years, times and end with a colon.
# Let's try it and see how well it works.
break if (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) ||
(l =~ /On \w+ \d+,? \d+,?.*wrote:/)
# Headers on subsequent lines
break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX }
# Headers on the same line
break if REPLYING_HEADER_LABELS.count { |label| l.include?(label) } >= 3
range_end = idx
end
lines[0..range_end].join.strip
end
def upload_attachments
attachments = []
message.attachments.each do |attachment|
tmp = Tempfile.new("gitlab-email-attachment")
begin
File.open(tmp.path, "w+b") { |f| f.write attachment.body.decoded }
file = {
tempfile: tmp,
filename: attachment.filename,
content_type: attachment.content_type
}
link = ::Projects::UploadService.new(sent_notification.project, file).execute
if link
text = "[#{link[:alt]}](#{link[:url]})"
text.prepend("!") if link[:is_image]
attachments << text
end
ensure
tmp.close!
end
end
attachments
end
end
end
require "spec_helper" require "spec_helper"
# Inspired in great part by Discourse's Email::Receiver # Inspired in great part by Discourse's Email::Receiver
describe Gitlab::EmailReceiver do describe Gitlab::Email::ReplyParser do
def fixture_file(filename) def fixture_file(filename)
return '' if filename.blank? return '' if filename.blank?
file_path = File.expand_path(Rails.root + 'spec/fixtures/' + filename) file_path = File.expand_path(Rails.root + 'spec/fixtures/' + filename)
File.read(file_path) File.read(file_path)
end end
before do describe 'self.parse_body' do
allow(Gitlab.config.reply_by_email).to receive(:enabled).and_return(true)
allow(Gitlab.config.reply_by_email).to receive(:address).and_return("reply+%{reply_key}@appmail.adventuretime.ooo")
end
describe 'parse_body' do
def test_parse_body(mail_string) def test_parse_body(mail_string)
Gitlab::EmailReceiver.new(nil).parse_body(Mail::Message.new(mail_string)) described_class.new(Mail::Message.new(mail_string)).execute
end end
it "raises EmptyEmailError if the message is blank" do it "returns an empty string if the message is blank" do
expect { test_parse_body("") }.to raise_error(Gitlab::EmailReceiver::EmptyEmailError) expect(test_parse_body("")).to eq("")
end end
it "raises EmptyEmailError if the message is not an email" do it "returns an empty string if the message is not an email" do
expect { test_parse_body("asdf" * 30) }.to raise_error(Gitlab::EmailReceiver::EmptyEmailError) expect(test_parse_body("asdf" * 30)).to eq("")
end end
it "raises EmptyEmailError if there is no reply content" do it "returns an empty string if there is no reply content" do
expect { test_parse_body(fixture_file("emails/no_content_reply.eml")) }.to raise_error(Gitlab::EmailReceiver::EmptyEmailError) expect(test_parse_body(fixture_file("emails/no_content_reply.eml"))).to eq("")
end end
it "can parse the html section" do it "can parse the html section" do
...@@ -193,289 +188,4 @@ This is a link http://example.com" ...@@ -193,289 +188,4 @@ This is a link http://example.com"
expect(test_parse_body(fixture_file("emails/outlook.eml"))).to eq("Microsoft Outlook 2010") expect(test_parse_body(fixture_file("emails/outlook.eml"))).to eq("Microsoft Outlook 2010")
end end
end end
# 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) { Gitlab::EmailReceiver.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
# expect(topic.posts.count).to eq(start_count + 1)
# created_post = topic.posts.last
# expect(created_post.via_email).to eq(true)
# expect(created_post.raw_email).to eq(fixture_file("emails/valid_reply.eml"))
# expect(created_post.cooked.strip).to eq(fixture_file("emails/valid_reply.cooked").strip)
# end
# end
# describe "paragraphs.eml" do
# let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
# let!(:email_raw) { fixture_file("emails/paragraphs.eml") }
# it "cooks multiple paragraphs with traditional Markdown linebreaks" do
# start_count = topic.posts.count
# receiver.process
# expect(topic.posts.count).to eq(start_count + 1)
# expect(topic.posts.last.cooked.strip).to eq(fixture_file("emails/paragraphs.cooked").strip)
# expect(topic.posts.last.cooked).not_to match /<br/
# end
# end
# 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
# Upload.stubs(:fix_image_orientation)
# ImageOptim.any_instance.stubs(:optimize_image!)
# start_count = topic.posts.count
# Upload.find_by(sha1: upload_sha).try(:destroy)
# receiver.process
# expect(topic.posts.count).to eq(start_count + 1)
# expect(topic.posts.last.cooked).to match /<img src=['"](\/uploads\/default\/original\/.+\.png)['"] width=['"]289['"] height=['"]126['"]>/
# expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil)
# end
# end
# # === Failure Conditions ===
# 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(Gitlab::EmailReceiver::InvalidPost)
# end
# end
# 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(Gitlab::EmailReceiver::InvalidPost)
# end
# end
# describe "auto response email replies should not be accepted" do
# let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
# let!(:email_raw) { fixture_file("emails/auto_reply.eml") }
# it "raises a AutoGeneratedEmailError" do
# expect { receiver.process }.to raise_error(Gitlab::EmailReceiver::AutoGeneratedEmailError)
# end
# end
# end
# describe "posting reply to a closed topic" 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) { Gitlab::EmailReceiver.new(email_raw) }
# let(:topic) { Fabricate(:topic, closed: true) }
# let(:post) { Fabricate(:post, topic: topic, post_number: 1) }
# 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: 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
# describe "should not create post" do
# let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
# let!(:email_raw) { fixture_file("emails/valid_reply.eml") }
# it "raises a TopicClosedError" do
# expect { receiver.process }.to raise_error(Gitlab::EmailReceiver::TopicClosedError)
# end
# end
# end
# describe "posting reply to a deleted topic" 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) { Gitlab::EmailReceiver.new(email_raw) }
# let(:deleted_topic) { Fabricate(:deleted_topic) }
# let(:post) { Fabricate(:post, topic: deleted_topic, post_number: 1) }
# 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: deleted_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
# describe "should not create post" do
# let!(:reply_key) { '59d8df8370b7e95c5a49fbf86aeb2c93' }
# let!(:email_raw) { fixture_file("emails/valid_reply.eml") }
# it "raises a TopicNotFoundError" do
# expect { receiver.process }.to raise_error(Gitlab::EmailReceiver::TopicNotFoundError)
# end
# end
# end
# 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) { Gitlab::EmailReceiver.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
# 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")
# }
# it "does not create a topic if the post fails" do
# before_topic_count = Topic.count
# expect { receiver.process }.to raise_error(Gitlab::EmailReceiver::InvalidPost)
# expect(Topic.count).to eq(before_topic_count)
# end
# end
# end
# 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])
# Gitlab::EmailReceiver.new(email).process
# end
# describe "with a valid email" do
# let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
# 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)
# }
# let(:receiver) { Gitlab::EmailReceiver.new(valid_reply) }
# 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'
# ) }
# 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
# it "extracts data" do
# expect{ receiver.process }.to raise_error(Gitlab::EmailReceiver::EmailLogNotFound)
# email_log.save!
# receiver.process
# expect(receiver.body).to eq(reply_body)
# expect(receiver.email_log).to eq(email_log)
# end
# end
# end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment