discourse/app/controllers/admin/email_controller.rb
Ted Johansson 7c0534c292
DEV: Replace raw comments with deprecation warnings (#22617)
We have a number of raw comments indicating that certain methods and classes are deprecated and marked for removal. This change turn those comments into deprecation warnings so that we can 1) see them in the logs of our own hosting and 2) give some warning to self hosters.
2023-07-18 10:13:40 +08:00

294 lines
8.9 KiB
Ruby

# frozen_string_literal: true
class Admin::EmailController < Admin::AdminController
def index
data = { delivery_method: delivery_method, settings: delivery_settings }
render_json_dump(data)
end
def test
params.require(:email_address)
begin
message = TestMailer.send_test(params[:email_address])
Email::Sender.new(message, :test_message).send
render json: { sent_test_email_message: I18n.t("admin.email.sent_test") }
rescue => e
render json: { errors: [e.message] }, status: 422
end
end
def sent
email_logs = EmailLog.joins(<<~SQL)
LEFT JOIN post_reply_keys
ON post_reply_keys.post_id = email_logs.post_id
AND post_reply_keys.user_id = email_logs.user_id
SQL
email_logs = filter_logs(email_logs, params)
if (reply_key = params[:reply_key]).present?
email_logs =
if reply_key.length == 32
email_logs.where("post_reply_keys.reply_key = ?", reply_key)
else
email_logs.where(
"replace(post_reply_keys.reply_key::VARCHAR, '-', '') ILIKE ?",
"%#{reply_key}%",
)
end
end
email_logs = email_logs.to_a
tuples = email_logs.map { |email_log| [email_log.post_id, email_log.user_id] }
reply_keys = {}
if tuples.present?
PostReplyKey
.where("(post_id,user_id) IN (#{(["(?)"] * tuples.size).join(", ")})", *tuples)
.pluck(:post_id, :user_id, "reply_key::text")
.each { |post_id, user_id, key| reply_keys[[post_id, user_id]] = key }
end
render_serialized(email_logs, EmailLogSerializer, reply_keys: reply_keys)
end
def skipped
skipped_email_logs = filter_logs(SkippedEmailLog, params)
render_serialized(skipped_email_logs, SkippedEmailLogSerializer)
end
def bounced
email_logs = filter_logs(EmailLog.bounced, params)
render_serialized(email_logs, EmailLogSerializer)
end
def received
incoming_emails = filter_incoming_emails(IncomingEmail, params)
render_serialized(incoming_emails, IncomingEmailSerializer)
end
def rejected
incoming_emails = filter_incoming_emails(IncomingEmail.errored, params)
render_serialized(incoming_emails, IncomingEmailSerializer)
end
def preview_digest
params.require(:last_seen_at)
params.require(:username)
user = User.find_by_username(params[:username])
raise Discourse::InvalidParameters unless user
renderer = Email::Renderer.new(UserNotifications.digest(user, since: params[:last_seen_at]))
render json: MultiJson.dump(html_content: renderer.html, text_content: renderer.text)
end
def advanced_test
params.require(:email)
receiver = Email::Receiver.new(params["email"])
text, elided, format = receiver.select_body
render json: success_json.merge!(text: text, elided: elided, format: format)
end
def send_digest
params.require(:last_seen_at)
params.require(:username)
params.require(:email)
user = User.find_by_username(params[:username])
message, skip_reason =
UserNotifications.public_send(:digest, user, since: params[:last_seen_at])
if message
message.to = params[:email]
begin
Email::Sender.new(message, :digest).send
render json: success_json
rescue => e
render json: { errors: [e.message] }, status: 422
end
else
render json: { errors: skip_reason }
end
end
def smtp_should_reject
params.require(:from)
params.require(:to)
# These strings aren't localized; they are sent to an anonymous SMTP user.
if !User.with_email(Email.downcase(params[:from])).exists? && !SiteSetting.enable_staged_users
render json: {
reject: true,
reason: "Mail from your address is not accepted. Do you have an account here?",
}
elsif Email::Receiver.check_address(Email.downcase(params[:to])).nil?
render json: {
reject: true,
reason:
"Mail to this address is not accepted. Check the address and try to send again?",
}
else
render json: { reject: false }
end
end
def handle_mail
deprecated_email_param_used = false
if params[:email_encoded].present?
email_raw = Base64.strict_decode64(params[:email_encoded])
elsif params[:email].present?
deprecated_email_param_used = true
email_raw = params[:email]
else
raise ActionController::ParameterMissing.new("email_encoded or email")
end
retry_count = 0
begin
Jobs.enqueue(
:process_email,
mail: email_raw,
retry_on_rate_limit: true,
source: "handle_mail",
)
rescue JSON::GeneratorError, Encoding::UndefinedConversionError => e
if retry_count == 0
email_raw = email_raw.force_encoding("iso-8859-1").encode("UTF-8")
retry_count += 1
retry
else
raise e
end
end
if deprecated_email_param_used
warning =
"warning: the email parameter is deprecated. all POST requests to this route should be sent with a base64 strict encoded email_encoded parameter instead. email has been received and is queued for processing"
Discourse.deprecate(warning, drop_from: "3.3.0")
render plain: warning
else
render plain: "email has been received and is queued for processing"
end
end
def incoming
params.require(:id)
incoming_email = IncomingEmail.find(params[:id].to_i)
serializer = IncomingEmailDetailsSerializer.new(incoming_email, root: false)
render_json_dump(serializer)
end
def incoming_from_bounced
params.require(:id)
begin
email_log = EmailLog.find_by(id: params[:id].to_i, bounced: true)
raise Discourse::InvalidParameters if email_log&.bounce_key.blank?
if Email::Sender.bounceable_reply_address?
bounced_to_address = Email::Sender.bounce_address(email_log.bounce_key)
incoming_email = IncomingEmail.find_by(to_addresses: bounced_to_address)
end
if incoming_email.nil?
email_local_part, email_domain = SiteSetting.notification_email.split("@")
bounced_to_address = "#{email_local_part}+verp-#{email_log.bounce_key}@#{email_domain}"
incoming_email = IncomingEmail.find_by(to_addresses: bounced_to_address)
end
# Temporary fix until all old format of emails has been purged via lib/email/cleaner.rb
if incoming_email.nil?
email_local_part, email_domain = SiteSetting.reply_by_email_address.split("@")
subdomain, root_domain, extension = email_domain&.split(".")
bounced_to_address = "#{subdomain}+verp-#{email_log.bounce_key}@#{root_domain}.#{extension}"
incoming_email = IncomingEmail.find_by(to_addresses: bounced_to_address)
end
raise Discourse::NotFound if incoming_email.nil?
serializer = IncomingEmailDetailsSerializer.new(incoming_email, root: false)
render_json_dump(serializer)
rescue => e
render json: { errors: [e.message] }, status: 404
end
end
private
def filter_logs(logs, params)
table_name = logs.table_name
logs =
logs
.includes(:user, post: :topic)
.references(:user)
.order(created_at: :desc)
.offset(params[:offset] || 0)
.limit(50)
logs = logs.where("users.username ILIKE ?", "%#{params[:user]}%") if params[:user].present?
logs = logs.where("#{table_name}.to_address ILIKE ?", "%#{params[:address]}%") if params[
:address
].present?
logs = logs.where("#{table_name}.email_type ILIKE ?", "%#{params[:type]}%") if params[
:type
].present?
if table_name == "email_logs" && params[:smtp_transaction_response].present?
logs =
logs.where(
"#{table_name}.smtp_transaction_response ILIKE ?",
"%#{params[:smtp_transaction_response]}%",
)
end
logs
end
def filter_incoming_emails(incoming_emails, params)
incoming_emails =
incoming_emails
.includes(:user, post: :topic)
.order(created_at: :desc)
.offset(params[:offset] || 0)
.limit(50)
incoming_emails = incoming_emails.where("from_address ILIKE ?", "%#{params[:from]}%") if params[
:from
].present?
incoming_emails =
incoming_emails.where(
"to_addresses ILIKE :to OR cc_addresses ILIKE :to",
to: "%#{params[:to]}%",
) if params[:to].present?
incoming_emails = incoming_emails.where("subject ILIKE ?", "%#{params[:subject]}%") if params[
:subject
].present?
incoming_emails = incoming_emails.where("error ILIKE ?", "%#{params[:error]}%") if params[
:error
].present?
incoming_emails
end
def delivery_settings
action_mailer_settings.reject { |k, _| k == :password }.map { |k, v| { name: k, value: v } }
end
def delivery_method
ActionMailer::Base.delivery_method
end
def action_mailer_settings
ActionMailer::Base.public_send "#{delivery_method}_settings"
end
end