123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
- class Channel::Driver::BaseEmailInbound < Channel::EmailParser
- ACTIVE_CHECK_INTERVAL = 20
- MessageResult = Struct.new(:success, :after_action, keyword_init: true)
- def fetchable?(_channel)
- true
- end
- def self.streamable?
- false
- end
- # Checks if the given channel was modified since it it was loaded
- # This check is used in email fetching loop
- def channel_has_changed?(channel)
- current_channel = Channel.find_by(id: channel.id)
- if !current_channel
- Rails.logger.info "Channel with id #{channel.id} is deleted in the meantime. Stop fetching."
- return true
- end
- return false if channel.updated_at == current_channel.updated_at
- Rails.logger.info "Channel with id #{channel.id} has changed. Stop fetching."
- true
- end
- # Fetches emails
- #
- # @param options [Hash]. See subclass for options
- # @param channel [Channel]
- #
- # @return [Hash]
- #
- # {
- # result: 'ok',
- # fetched: 123,
- # notice: 'e. g. message about to big emails in mailbox',
- # }
- def fetch(options, channel)
- @channel = channel
- @options = options
- @keep_on_server = ActiveModel::Type::Boolean.new.cast(options[:keep_on_server])
- setup_connection(options)
- collection, count_all = messages_iterator(@keep_on_server, options)
- count_fetched = 0
- too_large_messages = []
- result = 'ok'
- notice = ''
- collection.each.with_index(1) do |message_id, count|
- break if stop_fetching?(count)
- Rails.logger.info " - message #{count}/#{count_all}"
- message_result = fetch_single_message(message_id, count, count_all)
- count_fetched += 1 if message_result.success
- case message_result.after_action
- in [:too_large_ignored, message]
- notice += message
- too_large_messages << message
- in [:notice, message]
- notice += message
- in [:result_error, message]
- result = 'error'
- notice += message
- else
- end
- end
- fetch_wrap_up
- if count_all.zero?
- Rails.logger.info ' - no message'
- end
- # Error is raised if one of the messages was too large AND postmaster_send_reject_if_mail_too_large is turned off.
- # This effectivelly marks channels as stuck and gets highlighted for the admin.
- # New emails are still processed! But large email is not touched, so error keeps being re-raised on every fetch.
- if too_large_messages.present?
- raise too_large_messages.join("\n")
- end
- {
- result: result,
- fetched: count_fetched,
- notice: notice,
- }
- end
- def stop_fetching?(count)
- (count % ACTIVE_CHECK_INTERVAL).zero? && channel_has_changed?(@channel)
- end
- def fetch_wrap_up; end
- # Checks if mailbox has anything besides Zammad verification emails.
- # If any real messages exists, return the real count including messages to be ignored when importing.
- # If only verification messages found, return 0.
- #
- # @param options [Hash] driver-specific server setup. See #fetch in the corresponding driver.
- #
- # @return [Hash]
- #
- # {
- # result: 'ok'
- # content_messages: 123 # or 0 if there're none
- # }
- def check_configuration(options)
- setup_connection(options, check: true)
- Rails.logger.info 'check only mode, fetch no emails'
- collection, count_all = messages_iterator(false, options)
- has_content_messages = collection
- .any? do |message_id|
- validator = check_single_message(message_id)
- next if !validator
- !validator.verify_message? && !validator.ignore?
- end
- disconnect
- {
- result: 'ok',
- content_messages: has_content_messages ? count_all : 0,
- }
- end
- # Checks if probing email has arrived
- #
- # This method is used for custom IMAP only.
- # It is not used in conjunction with Micrsofot365 or Gogle OAuth channels.
- #
- # @param options [Hash] driver-specific server setup. See #fetch in the corresponding driver.
- # @param verify_string [String] to check with
- #
- # @return [Hash]
- #
- # {
- # result: 'ok' # or 'verify not ok' in case of failure
- # }
- def verify_transport(options, verify_string)
- setup_connection(options)
- collection, _count_all = messages_iterator(false, options, reverse: true)
- Rails.logger.info "verify mode, fetch no emails #{verify_string}"
- verify_regexp = %r{#{verify_string}}
- # check for verify message
- verify_message_id = collection.find do |message_id|
- verify_single_message(message_id, verify_regexp)
- end
- result = if verify_message_id
- Rails.logger.info " - verify email #{verify_string} found"
- verify_message_cleanup(verify_message_id)
- 'ok'
- else
- 'verify not ok'
- end
- disconnect
- { result:, }
- end
- end
|