mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 16:34:31 +08:00
FIX: when getting a reply by email, ensure it's by the same user
This commit is contained in:
@ -18,6 +18,8 @@ module Email
|
||||
class AutoGeneratedEmailError < ProcessingError; end
|
||||
class EmailLogNotFound < ProcessingError; end
|
||||
class InvalidPost < ProcessingError; end
|
||||
class ReplyUserNotFoundError < ProcessingError; end
|
||||
class ReplyUserNotMatchingError < ProcessingError; end
|
||||
|
||||
attr_reader :body, :email_log
|
||||
|
||||
@ -29,71 +31,78 @@ module Email
|
||||
def process
|
||||
raise EmptyEmailError if @raw.blank?
|
||||
|
||||
message = Mail.new(@raw)
|
||||
@message = Mail.new(@raw)
|
||||
|
||||
body = parse_body(message)
|
||||
raise AutoGeneratedEmailError if @message.header.to_s =~ /auto-(replied|generated)/
|
||||
|
||||
@body = parse_body(@message)
|
||||
|
||||
dest_info = { type: :invalid, obj: nil }
|
||||
# 'smtp_envelope_to' is a combination of: to, cc and bcc fields
|
||||
message.smtp_envelope_to.each do |to_address|
|
||||
dest_info = check_address(to_address)
|
||||
break if dest_info[:type] != :invalid
|
||||
end
|
||||
# prioriziting the `:reply` types
|
||||
dest_infos = @message.smtp_envelope_to
|
||||
.map { |to_address| check_address(to_address) }
|
||||
.compact
|
||||
.sort do |a, b|
|
||||
if a[:type] == :reply && b[:type] != :reply
|
||||
1
|
||||
elsif a[:type] != :reply && b[:type] == :reply
|
||||
-1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
raise BadDestinationAddress if dest_info[:type] == :invalid
|
||||
raise AutoGeneratedEmailError if message.header.to_s =~ /auto-generated/ || message.header.to_s =~ /auto-replied/
|
||||
|
||||
# TODO get to a state where we can remove this
|
||||
@message = message
|
||||
@body = body
|
||||
raise BadDestinationAddress if dest_infos.empty?
|
||||
|
||||
from = @message[:from].address_list.addresses.first
|
||||
user_email = "#{from.local}@#{from.domain}"
|
||||
user_name = from.display_name
|
||||
|
||||
@user = User.find_by_email(user_email)
|
||||
# TODO: deal with suspended/inactive users
|
||||
user = User.find_by_email(user_email)
|
||||
|
||||
# TODO: take advantage of all the "TO"s
|
||||
dest_info = dest_infos[0]
|
||||
case dest_info[:type]
|
||||
when :group
|
||||
group = dest_info[:obj]
|
||||
|
||||
if @user.blank?
|
||||
if user.blank?
|
||||
if SiteSetting.allow_staged_accounts
|
||||
@user = create_staged_account(user_email, user_name)
|
||||
user = create_staged_account(user_email, user_name)
|
||||
else
|
||||
wrap_body_in_quote(user_email)
|
||||
@user = Discourse.system_user
|
||||
user = Discourse.system_user
|
||||
end
|
||||
end
|
||||
|
||||
raise UserNotFoundError if @user.blank?
|
||||
raise UserNotSufficientTrustLevelError.new(@user) unless @user.has_trust_level?(TrustLevel[SiteSetting.email_in_min_trust.to_i])
|
||||
|
||||
create_new_topic(archetype: Archetype.private_message, target_group_names: [group.name])
|
||||
create_new_topic(user, archetype: Archetype.private_message, target_group_names: [group.name])
|
||||
when :category
|
||||
category = dest_info[:obj]
|
||||
|
||||
if @user.blank? && category.email_in_allow_strangers
|
||||
if user.blank? && category.email_in_allow_strangers
|
||||
if SiteSetting.allow_staged_accounts
|
||||
@user = create_staged_account(user_email)
|
||||
user = create_staged_account(user_email)
|
||||
else
|
||||
wrap_body_in_quote(user_email)
|
||||
@user = Discourse.system_user
|
||||
user = Discourse.system_user
|
||||
end
|
||||
end
|
||||
|
||||
raise UserNotFoundError if @user.blank?
|
||||
raise UserNotSufficientTrustLevelError.new(@user) unless category.email_in_allow_strangers || @user.has_trust_level?(TrustLevel[SiteSetting.email_in_min_trust.to_i])
|
||||
raise UserNotFoundError if user.blank?
|
||||
raise UserNotSufficientTrustLevelError.new(user) unless category.email_in_allow_strangers || user.has_trust_level?(TrustLevel[SiteSetting.email_in_min_trust.to_i])
|
||||
|
||||
create_new_topic(category: category.id)
|
||||
create_new_topic(user, category: category.id)
|
||||
when :reply
|
||||
@email_log = dest_info[:obj]
|
||||
|
||||
raise EmailLogNotFound if @email_log.blank?
|
||||
raise TopicNotFoundError if Topic.find_by_id(@email_log.topic_id).nil?
|
||||
raise TopicClosedError if Topic.find_by_id(@email_log.topic_id).closed?
|
||||
raise EmailLogNotFound if @email_log.blank?
|
||||
raise TopicNotFoundError if Topic.find_by_id(@email_log.topic_id).nil?
|
||||
raise TopicClosedError if Topic.find_by_id(@email_log.topic_id).closed?
|
||||
raise ReplyUserNotFoundError if user.blank?
|
||||
raise ReplyUserNotMatchingError if @email_log.user_id != user.id
|
||||
|
||||
create_reply
|
||||
create_reply(@email_log)
|
||||
end
|
||||
|
||||
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
|
||||
@ -110,37 +119,36 @@ module Email
|
||||
end
|
||||
|
||||
def check_address(address)
|
||||
# only check groups/categories when 'email_in' is enabled
|
||||
# only check for a group/category when 'email_in' is enabled
|
||||
if SiteSetting.email_in
|
||||
group = Group.find_by_email(address)
|
||||
return { type: :group, obj: group } if group
|
||||
return { address: address, type: :group, obj: group } if group
|
||||
|
||||
category = Category.find_by_email(address)
|
||||
return { type: :category, obj: category } if category
|
||||
return { address: address, type: :category, obj: category } if category
|
||||
end
|
||||
|
||||
regex = Regexp.escape(SiteSetting.reply_by_email_address)
|
||||
regex = regex.gsub(Regexp.escape('%{reply_key}'), "(.*)")
|
||||
regex = Regexp.new(regex)
|
||||
match = regex.match address
|
||||
match = reply_by_email_address_regex.match(address)
|
||||
if match && match[1].present?
|
||||
reply_key = match[1]
|
||||
email_log = EmailLog.for(reply_key)
|
||||
return { type: :reply, obj: email_log }
|
||||
email_log = EmailLog.for(match[1])
|
||||
return { address: address, type: :reply, obj: email_log }
|
||||
end
|
||||
end
|
||||
|
||||
{ type: :invalid, obj: nil }
|
||||
def reply_by_email_address_regex
|
||||
@reply_by_email_address_regex ||= Regexp.new Regexp.escape(SiteSetting.reply_by_email_address)
|
||||
.gsub(Regexp.escape("%{reply_key}"), "([[:xdigit:]]{32})")
|
||||
end
|
||||
|
||||
def parse_body(message)
|
||||
body = select_body message
|
||||
body = select_body(message)
|
||||
encoding = body.encoding
|
||||
raise EmptyEmailError if body.strip.blank?
|
||||
|
||||
body = discourse_email_trimmer body
|
||||
body = discourse_email_trimmer(body)
|
||||
raise EmptyEmailError if body.strip.blank?
|
||||
|
||||
body = DiscourseEmailParser.parse_reply body
|
||||
body = DiscourseEmailParser.parse_reply(body)
|
||||
raise EmptyEmailError if body.strip.blank?
|
||||
|
||||
body.force_encoding(encoding).encode("UTF-8")
|
||||
@ -187,7 +195,7 @@ module Email
|
||||
nil
|
||||
end
|
||||
|
||||
REPLYING_HEADER_LABELS = ['From', 'Sent', 'To', 'Subject', 'Reply To', 'Cc', 'Bcc', 'Date']
|
||||
REPLYING_HEADER_LABELS = ['From', 'Sent', 'To', 'Subject', 'In-Reply-To', 'Cc', 'Bcc', 'Date']
|
||||
REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |lbl| "#{lbl}:" })
|
||||
|
||||
def line_is_quote?(l)
|
||||
@ -230,25 +238,25 @@ module Email
|
||||
@body = "[quote=\"#{user_email}\"]\n#{@body}\n[/quote]"
|
||||
end
|
||||
|
||||
def create_reply
|
||||
create_post_with_attachments(@email_log.user,
|
||||
def create_reply(email_log)
|
||||
create_post_with_attachments(email_log.user,
|
||||
raw: @body,
|
||||
topic_id: @email_log.topic_id,
|
||||
reply_to_post_number: @email_log.post.post_number)
|
||||
topic_id: email_log.topic_id,
|
||||
reply_to_post_number: email_log.post.post_number)
|
||||
end
|
||||
|
||||
def create_new_topic(topic_options={})
|
||||
def create_new_topic(user, topic_options={})
|
||||
topic_options[:raw] = @body
|
||||
topic_options[:title] = @message.subject
|
||||
|
||||
result = create_post_with_attachments(@user, topic_options)
|
||||
result = create_post_with_attachments(user, topic_options)
|
||||
topic_id = result.post.present? ? result.post.topic_id : nil
|
||||
|
||||
EmailLog.create(
|
||||
email_type: "topic_via_incoming_email",
|
||||
to_address: @message.from.first, # pick from address because we want the user's email
|
||||
to_address: user.email,
|
||||
topic_id: topic_id,
|
||||
user_id: @user.id,
|
||||
user_id: user.id,
|
||||
)
|
||||
|
||||
result
|
||||
|
Reference in New Issue
Block a user