2015-08-20 14:05:06 -04:00
|
|
|
# Inspired in great part by Discourse's Email::Receiver
|
|
|
|
module Gitlab
|
|
|
|
module Email
|
|
|
|
class Receiver
|
|
|
|
class ProcessingError < StandardError; end
|
|
|
|
class EmailUnparsableError < ProcessingError; end
|
2015-08-20 14:33:18 -04:00
|
|
|
class SentNotificationNotFoundError < ProcessingError; end
|
2015-08-20 14:05:06 -04:00
|
|
|
class EmptyEmailError < ProcessingError; end
|
2015-08-20 14:33:18 -04:00
|
|
|
class AutoGeneratedEmailError < ProcessingError; end
|
2015-08-20 14:05:06 -04:00
|
|
|
class UserNotFoundError < ProcessingError; end
|
2015-08-21 13:14:45 -04:00
|
|
|
class UserBlockedError < ProcessingError; end
|
2015-08-20 14:05:06 -04:00
|
|
|
class UserNotAuthorizedError < ProcessingError; end
|
|
|
|
class NoteableNotFoundError < ProcessingError; end
|
2015-08-20 14:33:18 -04:00
|
|
|
class InvalidNoteError < ProcessingError; end
|
2015-08-20 14:05:06 -04:00
|
|
|
|
|
|
|
def initialize(raw)
|
|
|
|
@raw = raw
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
|
|
|
raise EmptyEmailError if @raw.blank?
|
|
|
|
|
2015-08-20 15:17:59 -04:00
|
|
|
raise SentNotificationNotFoundError unless sent_notification
|
|
|
|
|
2015-08-20 14:05:06 -04:00
|
|
|
raise AutoGeneratedEmailError if message.header.to_s =~ /auto-(generated|replied)/
|
|
|
|
|
|
|
|
author = sent_notification.recipient
|
|
|
|
|
|
|
|
raise UserNotFoundError unless author
|
|
|
|
|
2015-08-21 13:14:45 -04:00
|
|
|
raise UserBlockedError if author.blocked?
|
|
|
|
|
2015-08-20 14:05:06 -04:00
|
|
|
project = sent_notification.project
|
|
|
|
|
2015-08-20 15:17:59 -04:00
|
|
|
raise UserNotAuthorizedError unless project && author.can?(:create_note, project)
|
2015-08-20 14:05:06 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2015-08-20 14:33:18 -04:00
|
|
|
raise InvalidNoteError, message
|
2015-08-20 14:05:06 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2015-08-20 15:17:59 -04:00
|
|
|
def message
|
|
|
|
@message ||= Mail::Message.new(@raw)
|
|
|
|
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
|
|
|
|
raise EmailUnparsableError, e
|
|
|
|
end
|
|
|
|
|
2015-08-20 14:05:06 -04:00
|
|
|
def reply_key
|
|
|
|
reply_key = nil
|
|
|
|
message.to.each do |address|
|
2015-09-21 03:46:47 -04:00
|
|
|
reply_key = Gitlab::IncomingEmail.key_from_address(address)
|
2015-08-20 14:05:06 -04:00
|
|
|
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)
|
2015-08-20 14:17:14 -04:00
|
|
|
attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project)
|
2015-08-20 14:05:06 -04:00
|
|
|
|
|
|
|
attachments.each do |link|
|
|
|
|
text = "[#{link[:alt]}](#{link[:url]})"
|
|
|
|
text.prepend("!") if link[:is_image]
|
|
|
|
|
|
|
|
reply << "\n\n#{text}"
|
|
|
|
end
|
2015-08-20 14:17:14 -04:00
|
|
|
|
|
|
|
reply
|
2015-08-20 14:05:06 -04:00
|
|
|
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,
|
2015-09-21 03:46:47 -04:00
|
|
|
commit_id: sent_notification.commit_id,
|
|
|
|
line_code: sent_notification.line_code
|
2015-08-20 14:05:06 -04:00
|
|
|
).execute
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|