base_email_inbound.rb 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. class Channel::Driver::BaseEmailInbound < Channel::EmailParser
  3. ACTIVE_CHECK_INTERVAL = 20
  4. MessageResult = Struct.new(:success, :after_action, keyword_init: true)
  5. def fetchable?(_channel)
  6. true
  7. end
  8. def self.streamable?
  9. false
  10. end
  11. # Checks if the given channel was modified since it it was loaded
  12. # This check is used in email fetching loop
  13. def channel_has_changed?(channel)
  14. current_channel = Channel.find_by(id: channel.id)
  15. if !current_channel
  16. Rails.logger.info "Channel with id #{channel.id} is deleted in the meantime. Stop fetching."
  17. return true
  18. end
  19. return false if channel.updated_at == current_channel.updated_at
  20. Rails.logger.info "Channel with id #{channel.id} has changed. Stop fetching."
  21. true
  22. end
  23. # Fetches emails
  24. #
  25. # @param options [Hash]. See subclass for options
  26. # @param channel [Channel]
  27. #
  28. # @return [Hash]
  29. #
  30. # {
  31. # result: 'ok',
  32. # fetched: 123,
  33. # notice: 'e. g. message about to big emails in mailbox',
  34. # }
  35. def fetch(options, channel)
  36. @channel = channel
  37. @options = options
  38. @keep_on_server = ActiveModel::Type::Boolean.new.cast(options[:keep_on_server])
  39. setup_connection(options)
  40. collection, count_all = messages_iterator(@keep_on_server, options)
  41. count_fetched = 0
  42. too_large_messages = []
  43. result = 'ok'
  44. notice = ''
  45. collection.each.with_index(1) do |message_id, count|
  46. break if stop_fetching?(count)
  47. Rails.logger.info " - message #{count}/#{count_all}"
  48. message_result = fetch_single_message(message_id, count, count_all)
  49. count_fetched += 1 if message_result.success
  50. case message_result.after_action
  51. in [:too_large_ignored, message]
  52. notice += message
  53. too_large_messages << message
  54. in [:notice, message]
  55. notice += message
  56. in [:result_error, message]
  57. result = 'error'
  58. notice += message
  59. else
  60. end
  61. end
  62. fetch_wrap_up
  63. if count_all.zero?
  64. Rails.logger.info ' - no message'
  65. end
  66. # Error is raised if one of the messages was too large AND postmaster_send_reject_if_mail_too_large is turned off.
  67. # This effectivelly marks channels as stuck and gets highlighted for the admin.
  68. # New emails are still processed! But large email is not touched, so error keeps being re-raised on every fetch.
  69. if too_large_messages.present?
  70. raise too_large_messages.join("\n")
  71. end
  72. {
  73. result: result,
  74. fetched: count_fetched,
  75. notice: notice,
  76. }
  77. end
  78. def stop_fetching?(count)
  79. (count % ACTIVE_CHECK_INTERVAL).zero? && channel_has_changed?(@channel)
  80. end
  81. def fetch_wrap_up; end
  82. # Checks if mailbox has anything besides Zammad verification emails.
  83. # If any real messages exists, return the real count including messages to be ignored when importing.
  84. # If only verification messages found, return 0.
  85. #
  86. # @param options [Hash] driver-specific server setup. See #fetch in the corresponding driver.
  87. #
  88. # @return [Hash]
  89. #
  90. # {
  91. # result: 'ok'
  92. # content_messages: 123 # or 0 if there're none
  93. # }
  94. def check_configuration(options)
  95. setup_connection(options, check: true)
  96. Rails.logger.info 'check only mode, fetch no emails'
  97. collection, count_all = messages_iterator(false, options)
  98. has_content_messages = collection
  99. .any? do |message_id|
  100. validator = check_single_message(message_id)
  101. next if !validator
  102. !validator.verify_message? && !validator.ignore?
  103. end
  104. disconnect
  105. {
  106. result: 'ok',
  107. content_messages: has_content_messages ? count_all : 0,
  108. }
  109. end
  110. # Checks if probing email has arrived
  111. #
  112. # This method is used for custom IMAP only.
  113. # It is not used in conjunction with Micrsofot365 or Gogle OAuth channels.
  114. #
  115. # @param options [Hash] driver-specific server setup. See #fetch in the corresponding driver.
  116. # @param verify_string [String] to check with
  117. #
  118. # @return [Hash]
  119. #
  120. # {
  121. # result: 'ok' # or 'verify not ok' in case of failure
  122. # }
  123. def verify_transport(options, verify_string)
  124. setup_connection(options)
  125. collection, _count_all = messages_iterator(false, options, reverse: true)
  126. Rails.logger.info "verify mode, fetch no emails #{verify_string}"
  127. verify_regexp = %r{#{verify_string}}
  128. # check for verify message
  129. verify_message_id = collection.find do |message_id|
  130. verify_single_message(message_id, verify_regexp)
  131. end
  132. result = if verify_message_id
  133. Rails.logger.info " - verify email #{verify_string} found"
  134. verify_message_cleanup(verify_message_id)
  135. 'ok'
  136. else
  137. 'verify not ok'
  138. end
  139. disconnect
  140. { result:, }
  141. end
  142. end