مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-12-01 05:43:04 +00:00
191 أسطر
7.0 KiB
Ruby
191 أسطر
7.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module MessageDequeuer
|
|
class OutgoingMessageProcessor < Base
|
|
|
|
def process
|
|
catch_stops do
|
|
check_domain
|
|
check_rcpt_to
|
|
add_tag
|
|
hold_if_credential_is_set_to_hold
|
|
hold_if_recipient_on_suppression_list
|
|
parse_content
|
|
inspect_message
|
|
fail_if_spam
|
|
add_outgoing_headers
|
|
check_send_limits
|
|
increment_live_stats
|
|
hold_if_server_development_mode
|
|
send_message_to_sender
|
|
add_recipient_to_suppression_list_on_too_many_hard_fails
|
|
remove_recipient_from_suppression_list_on_success
|
|
log_sender_result
|
|
finish_processing
|
|
end
|
|
rescue StandardError => e
|
|
handle_exception(e)
|
|
end
|
|
|
|
private
|
|
|
|
def check_domain
|
|
return if queued_message.message.domain
|
|
|
|
log "message has no domain, hard failing"
|
|
create_delivery "HardFail", details: "Message's domain no longer exist"
|
|
remove_from_queue
|
|
stop_processing
|
|
end
|
|
|
|
def check_rcpt_to
|
|
return unless queued_message.message.rcpt_to.blank?
|
|
|
|
log "message has no 'to' address, hard failing"
|
|
create_delivery "HardFail", details: "Message doesn't have an RCPT to"
|
|
remove_from_queue
|
|
stop_processing
|
|
end
|
|
|
|
def add_tag
|
|
return if queued_message.message.tag
|
|
return unless tag = queued_message.message.headers["x-postal-tag"]
|
|
|
|
log "added tag: #{tag.last}"
|
|
queued_message.message.update(tag: tag.last)
|
|
end
|
|
|
|
def hold_if_credential_is_set_to_hold
|
|
return if queued_message.manual?
|
|
return if queued_message.message.credential.nil?
|
|
return unless queued_message.message.credential.hold?
|
|
|
|
log "credential wants us to hold messages, holding"
|
|
create_delivery "Held", details: "Credential is configured to hold all messages authenticated by it."
|
|
remove_from_queue
|
|
stop_processing
|
|
end
|
|
|
|
def hold_if_recipient_on_suppression_list
|
|
return if queued_message.manual?
|
|
return unless sl = queued_message.server.message_db.suppression_list.get(:recipient, queued_message.message.rcpt_to)
|
|
|
|
log "recipient is on the suppression list, holding"
|
|
create_delivery "Held", details: "Recipient (#{queued_message.message.rcpt_to}) is on the suppression list (reason: #{sl['reason']})"
|
|
remove_from_queue
|
|
stop_processing
|
|
end
|
|
|
|
def parse_content
|
|
return unless queued_message.message.should_parse?
|
|
|
|
log "parsing message content as it hasn't been parsed before"
|
|
queued_message.message.parse_content
|
|
end
|
|
|
|
def inspect_message
|
|
return if queued_message.message.inspected
|
|
return unless queued_message.server.outbound_spam_threshold
|
|
|
|
log "inspecting message"
|
|
queued_message.message.inspect_message
|
|
return unless queued_message.message.inspected
|
|
|
|
if queued_message.message.spam_score >= queued_message.server.outbound_spam_threshold
|
|
queued_message.message.update(spam: true)
|
|
end
|
|
|
|
log "message inspected successfully", spam: queued_message.message.spam?, spam_score: queued_message.message.spam_score
|
|
end
|
|
|
|
def fail_if_spam
|
|
return unless queued_message.message.spam
|
|
|
|
log "message is spam (#{queued_message.message.spam_score}), hard failing", server_threshold: queued_message.server.outbound_spam_threshold
|
|
create_delivery "HardFail",
|
|
details: "Message is likely spam. Threshold is #{queued_message.server.outbound_spam_threshold} and " \
|
|
"the message scored #{queued_message.message.spam_score}."
|
|
remove_from_queue
|
|
stop_processing
|
|
end
|
|
|
|
def add_outgoing_headers
|
|
return if queued_message.message.has_outgoing_headers?
|
|
|
|
queued_message.message.add_outgoing_headers
|
|
end
|
|
|
|
def check_send_limits
|
|
if queued_message.server.send_limit_exceeded?
|
|
# If we're over the limit, we're going to be holding this message
|
|
log "server send limit has been exceeded, holding", send_limit: queued_message.server.send_limit
|
|
queued_message.server.update_columns(send_limit_exceeded_at: Time.now, send_limit_approaching_at: nil)
|
|
create_delivery "Held", details: "Message held because send limit (#{queued_message.server.send_limit}) has been reached."
|
|
remove_from_queue
|
|
stop_processing
|
|
elsif queued_message.server.send_limit_approaching?
|
|
# If we're approaching the limit, just say we are but continue to process the message
|
|
queued_message.server.update_columns(send_limit_approaching_at: Time.now, send_limit_exceeded_at: nil)
|
|
else
|
|
queued_message.server.update_columns(send_limit_approaching_at: nil, send_limit_exceeded_at: nil)
|
|
end
|
|
end
|
|
|
|
def send_message_to_sender
|
|
@result = @state.send_result
|
|
return if @result
|
|
|
|
sender = @state.sender_for(SMTPSender,
|
|
queued_message.message.recipient_domain,
|
|
queued_message.ip_address)
|
|
|
|
@result = sender.send_message(queued_message.message)
|
|
return unless @result.connect_error
|
|
|
|
@state.send_result = @result
|
|
end
|
|
|
|
def add_recipient_to_suppression_list_on_too_many_hard_fails
|
|
return unless @result.type == "HardFail"
|
|
|
|
recent_hard_fails = queued_message.server.message_db.select(:messages,
|
|
where: {
|
|
rcpt_to: queued_message.message.rcpt_to,
|
|
status: "HardFail",
|
|
timestamp: { greater_than: 24.hours.ago.to_f }
|
|
},
|
|
count: true)
|
|
return if recent_hard_fails < 1
|
|
|
|
added = queued_message.server.message_db.suppression_list.add(:recipient, queued_message.message.rcpt_to,
|
|
reason: "too many hard fails")
|
|
return unless added
|
|
|
|
log "Added #{queued_message.message.rcpt_to} to suppression list because #{recent_hard_fails} hard fails in 24 hours"
|
|
@additional_delivery_details = "Recipient added to suppression list (too many hard fails)"
|
|
end
|
|
|
|
def remove_recipient_from_suppression_list_on_success
|
|
return unless @result.type == "Sent"
|
|
|
|
removed = queued_message.server.message_db.suppression_list.remove(:recipient, queued_message.message.rcpt_to)
|
|
return unless removed
|
|
|
|
log "removed #{queued_message.message.rcpt_to} from suppression list"
|
|
@additional_delivery_details = "Recipient removed from suppression list"
|
|
end
|
|
|
|
def finish_processing
|
|
if @result.retry
|
|
queued_message.retry_later(@result.retry.is_a?(Integer) ? @result.retry : nil)
|
|
log "message requeued for trying later", retry_after: queued_message.retry_after
|
|
stop_processing
|
|
end
|
|
|
|
log "message processing complete"
|
|
remove_from_queue
|
|
end
|
|
|
|
end
|
|
end
|