░▒▓████████████████████████████████▓▒░ ░▒▓█ ▓▒░ ░▒▓█ ~ S W A M P ~ ▓▒░ ░▒▓█ ▓▒░ ░▒▓████████████████████████████████▓▒░
pplmvsvm
LOCATION:
/var/www/canvas/app/models/incoming_mail
☗ ROOT
↻ REFRESH
✎ CARVE FLESH
EDITING: message_handler.rb
# frozen_string_literal: true # # Copyright (C) 2014 - present Instructure, Inc. # # This file is part of Canvas. # # Canvas is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the Free # Software Foundation, version 3 of the License. # # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more # details. # # You should have received a copy of the GNU Affero General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # module IncomingMail class MessageHandler def handle(outgoing_from_address, body, html_body, incoming_message, tag) secure_id, original_message_id, timestamp = parse_tag(tag) return unless original_message_id original_message = get_original_message(original_message_id, timestamp) # This prevents us from rebouncing users that have auto-replies setup -- only bounce something # that was sent out because of a notification. return unless original_message&.notification_id return unless valid_secure_id?(original_message_id, secure_id) from_channel = nil original_message.shard.activate do context = original_message.context user = original_message.user raise IncomingMail::Errors::UnknownAddress unless valid_user_and_context?(context, user) raise IncomingMail::Errors::UserSuspended if user.suspended? from_channel = sent_from_channel(user, incoming_message) raise IncomingMail::Errors::UnknownSender unless from_channel raise IncomingMail::Errors::MessageTooLong if body.length > ActiveRecord::Base.maximum_text_length raise IncomingMail::Errors::MessageTooLong if html_body.length > ActiveRecord::Base.maximum_text_length raise IncomingMail::Errors::BlankMessage if body.blank? Rails.cache.fetch(["incoming_mail_reply_from", context, incoming_message.message_id].cache_key, expires_in: 7.days) do context.reply_from({ purpose: "general", user:, subject: IncomingMailProcessor::IncomingMessageProcessor.utf8ify(incoming_message.subject, incoming_message.header[:subject].try(:charset)), html: html_body, text: body }) true end rescue IncomingMail::Errors::ReplyFrom => e bounce_message(original_message, incoming_message, e, outgoing_from_address, from_channel) rescue => e Canvas::Errors.capture_exception("IncomingMailProcessor", e) end end private def bounce_message(original_message, incoming_message, error, outgoing_from_address, from_channel) incoming_from = from_channel.try(:path) || incoming_message.from.try(:first) incoming_subject = incoming_message.subject return unless incoming_from ndr_subject, ndr_body = bounce_message_strings(incoming_subject, error) outgoing_message = Message.new({ to: incoming_from, from: outgoing_from_address, subject: ndr_subject, body: ndr_body, delay_for: 0, context: nil, path_type: "email", from_name: "Instructure", }) outgoing_message_delivered = false original_message.shard.activate do comch = from_channel || CommunicationChannel.active.email.by_path(incoming_from).first outgoing_message.communication_channel = comch outgoing_message.user = comch.try(:user) if outgoing_message.communication_channel outgoing_message.save outgoing_message.deliver outgoing_message_delivered = true end end unless outgoing_message_delivered # Can't use our usual mechanisms, so just try to send it once now begin Mailer.deliver(Mailer.create_message(outgoing_message)) rescue # TODO: put some kind of error logging here? end end end def bounce_message_strings(subject, error) ndr_subject = I18n.t("Undelivered message") ndr_body = case error when IncomingMail::Errors::ReplyToDeletedDiscussion InstStatsd::Statsd.increment("incoming_mail_processor.message_processing_error.reply_to_deleted_discussion") I18n.t(<<~TEXT, subject:).gsub(/^ +/, "") The message titled "%{subject}" could not be delivered because the discussion topic has been deleted. If you are trying to contact someone through Canvas you can try logging in to your account and sending them a message using the Inbox tool. Thank you, Canvas Support TEXT when IncomingMail::Errors::ReplyToLockedTopic InstStatsd::Statsd.increment("incoming_mail_processor.message_processing_error.reply_to_locked_topic") I18n.t("lib.incoming_message_processor.locked_topic.body", <<~TEXT, subject:).gsub(/^ +/, "") The message titled "%{subject}" could not be delivered because the discussion topic is locked. If you are trying to contact someone through Canvas you can try logging in to your account and sending them a message using the Inbox tool. Thank you, Canvas Support TEXT when IncomingMail::Errors::UnknownSender InstStatsd::Statsd.increment("incoming_mail_processor.message_processing_error.unknown_sender") I18n.t(<<~TEXT, subject:, link: I18n.t(:"community.guides_home")).gsub(/^ +/, "") The message you sent with the subject line "%{subject}" was not delivered. To reply to Canvas messages from this email, it must first be a confirmed communication channel in your Canvas profile. Please visit your profile and resend the confirmation email for this email address. You may also contact this person via the Canvas Inbox. For help, please see the Inbox chapter for your user role in the Canvas Guides. [See %{link}]. Thank you, Canvas Support TEXT when IncomingMail::Errors::UserSuspended InstStatsd::Statsd.increment("incoming_mail_processor.message_processing_error.user_suspended") I18n.t(<<~TEXT, subject:).gsub(/^ +/, "") The message you sent with the subject line "%{subject}" was not delivered because your account has been suspended. Thank you, Canvas Support TEXT else # including IncomingMessageProcessor::UnknownAddressError InstStatsd::Statsd.increment("incoming_mail_processor.message_processing_error.catch_all") error_info = { tags: { type: :message_processing_error_catch_all }, ref: get_ref_uuid } Canvas::Errors.capture(error, error_info, :error) I18n.t("lib.incoming_message_processor.failure_message.body", <<~TEXT, subject:, ref: error_info[:ref]).gsub(/^ +/, "") The message titled "%{subject}" could not be delivered. The message was sent to an unknown mailbox address. If you are trying to contact someone through Canvas you can try logging in to your account and sending them a message using the Inbox tool. Thank you, Canvas Support Reference: %{ref} TEXT end [ndr_subject, ndr_body] end def valid_secure_id?(original_message_id, secure_id) options = {} options[:truncate] = 16 if secure_id.length == 16 Canvas::Security.verify_hmac_sha1(secure_id, original_message_id, options) end def valid_user_and_context?(context, user) user && context && context.respond_to?(:reply_from) end def sent_from_channel(user, incoming_message) from_addresses = ((incoming_message.from || []) + (incoming_message.reply_to || [])).uniq user && from_addresses.lazy.map { |addr| user.communication_channels.active.email.by_path(addr).first }.first end def parse_tag(tag) match = tag.match(/^(\h+)-([0-9~]+)(?:-([0-9]+))?$/) return match[1], match[2], match[3] if match end def get_original_message(original_message_id, timestamp) if timestamp Message.where(id: original_message_id).at_timestamp(timestamp).first else Message.where(id: original_message_id).first end end def get_ref_uuid SecureRandom.uuid end end end
CANCEL
Name
Type
Size
Modified
Actions
↩ ..
DIR
—
—
📄 errors.rb
RB
1.1 KB
2023-09-24 03:36
EDIT
📄 message_handler.rb
RB
9 KB
2023-09-24 03:39
EDIT
📄 reply_to_address.rb
RB
2.6 KB
2023-09-24 03:36
EDIT