email_postmaster_to_sender.rb 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
  2. require 'test_helper'
  3. require 'net/imap'
  4. class EmailPostmasterToSender < ActiveSupport::TestCase
  5. setup do
  6. Setting.set('postmaster_max_size', 0.1)
  7. @test_id = rand(999_999_999)
  8. # setup the IMAP account info for Zammad
  9. if ENV['MAIL_SERVER'].blank?
  10. raise "Need MAIL_SERVER as ENV variable like export MAIL_SERVER='mx.example.com'"
  11. end
  12. if ENV['MAIL_SERVER_ACCOUNT'].blank?
  13. raise "Need MAIL_SERVER_ACCOUNT as ENV variable like export MAIL_SERVER_ACCOUNT='user:somepass'"
  14. end
  15. @server_address = ENV['MAIL_SERVER']
  16. @server_login = ENV['MAIL_SERVER_ACCOUNT'].split(':')[0]
  17. @server_password = ENV['MAIL_SERVER_ACCOUNT'].split(':')[1]
  18. @folder = "postmaster_to_sender_#{@test_id}"
  19. if ENV['MAIL_SERVER_EMAIL'].blank?
  20. raise "Need MAIL_SERVER_EMAIL as ENV variable like export MAIL_SERVER_EMAIL='admin@example.com'"
  21. end
  22. @sender_email_address = ENV['MAIL_SERVER_EMAIL']
  23. @email_address = EmailAddress.create!(
  24. realname: 'me Helpdesk',
  25. email: "some-zammad-#{ENV['MAIL_SERVER_EMAIL']}",
  26. updated_by_id: 1,
  27. created_by_id: 1,
  28. )
  29. group = Group.create_or_update(
  30. name: 'PostmasterToSenderTest',
  31. email_address_id: @email_address.id,
  32. updated_by_id: 1,
  33. created_by_id: 1,
  34. )
  35. @channel = Channel.create!(
  36. area: 'Email::Account',
  37. group_id: group.id,
  38. options: {
  39. inbound: {
  40. adapter: 'imap',
  41. options: {
  42. host: @server_address,
  43. user: @server_login,
  44. password: @server_password,
  45. ssl: true,
  46. folder: @folder,
  47. keep_on_server: false,
  48. }
  49. },
  50. outbound: {
  51. adapter: 'smtp',
  52. options: {
  53. host: @server_address,
  54. port: 25,
  55. start_tls: true,
  56. user: @server_login,
  57. password: @server_password,
  58. email: @email_address.email
  59. },
  60. },
  61. },
  62. active: true,
  63. updated_by_id: 1,
  64. created_by_id: 1,
  65. )
  66. @email_address.channel_id = @channel.id
  67. @email_address.save!
  68. end
  69. test 'postmaster reply with email on oversized incoming emails' do
  70. imap = Net::IMAP.new(@server_address, 993, true, nil, false)
  71. imap.login(@server_login, @server_password)
  72. imap.create(@folder)
  73. imap.select(@folder)
  74. # put a very large message in it
  75. large_message = "Subject: Oversized Email Message
  76. From: Max Mustermann <#{@sender_email_address}>
  77. To: shugo@example.com
  78. Message-ID: <#{@test_id}@zammad.test.com>
  79. Oversized Email Message Body #{'#' * 120_000}
  80. ".gsub(%r{\n}, "\r\n")
  81. large_message_md5 = Digest::MD5.hexdigest(large_message)
  82. large_message_size = format('%<MB>.2f', MB: large_message.size.to_f / 1024 / 1024)
  83. imap.append(@folder, large_message, [], Time.zone.now)
  84. @channel.fetch(true)
  85. # 1. verify that the oversized email has been saved locally to:
  86. # /tmp/oversized_mail/yyyy-mm-ddThh:mm:ss-:md5.eml
  87. path = Rails.root.join('tmp/oversized_mail')
  88. target_files = Dir.entries(path).select do |filename|
  89. filename =~ %r{^#{large_message_md5}\.eml$}
  90. end
  91. assert(target_files.present?, 'Large message .eml log file must be present.')
  92. # pick the latest file that matches the criteria
  93. target_file = target_files.max
  94. # verify that the file is byte for byte identical to the sent message
  95. file_path = Rails.root.join('tmp/oversized_mail', target_file)
  96. eml_data = File.read(file_path)
  97. assert_equal(large_message, eml_data)
  98. # 2. verify that a postmaster response email has been sent to the sender
  99. message_ids = nil
  100. 5.times do |sleep_offset|
  101. imap.select('inbox')
  102. message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
  103. break if message_ids.count.positive?
  104. # send mail hasn't arrived yet in the inbox
  105. sleep sleep_offset
  106. end
  107. assert(message_ids.count.positive?, 'Must have received a reply from the postmaster')
  108. imap_message_id = message_ids.last
  109. msg = imap.fetch(imap_message_id, 'RFC822')[0].attr['RFC822']
  110. assert(msg.present?, 'Must have received a reply from the postmaster')
  111. imap.store(imap_message_id, '+FLAGS', [:Deleted])
  112. imap.expunge
  113. # parse the reply mail and verify the various headers
  114. parser = Channel::EmailParser.new
  115. mail = parser.parse(msg)
  116. assert_equal(mail[:from_email], @email_address.email)
  117. assert_equal(mail[:subject], '[undeliverable] Message too large')
  118. assert_equal("<#{@test_id}@zammad.test.com>",
  119. mail['references'],
  120. 'Reply\'s Referecnes header must match the send message ID')
  121. assert_equal("<#{@test_id}@zammad.test.com>",
  122. mail['in-reply-to'],
  123. 'Reply\'s In-Reply-To header must match the send message ID')
  124. # verify the reply mail body content
  125. body = mail[:body]
  126. assert(body.start_with?('Dear Max Mustermann'), 'Body must contain sender name')
  127. assert(body.include?('Oversized Email Message'), 'Body must contain original subject')
  128. assert(body.include?('0.1 MB'), 'Body must contain max allowed message size')
  129. assert(body.include?("#{large_message_size} MB"), 'Body must contain the original message size')
  130. assert(body.include?(Setting.get('fqdn')), 'Body must contain the Zammad instance name')
  131. # 3. check if original mail got removed
  132. imap.select(@folder)
  133. message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
  134. assert_equal(message_ids.count, 0, 'Original customer mail must be deleted.')
  135. # final clean up
  136. imap.delete(@folder)
  137. @channel.destroy!
  138. end
  139. test 'postmaster reply with no email on oversized incoming emails' do
  140. Setting.set('postmaster_send_reject_if_mail_too_large', false)
  141. imap = Net::IMAP.new(@server_address, 993, true, nil, false)
  142. imap.login(@server_login, @server_password)
  143. imap.select('inbox')
  144. message_count = imap.sort(['DATE'], ['ALL'], 'US-ASCII').count
  145. imap.create(@folder)
  146. imap.select(@folder)
  147. # put a very large message in it
  148. large_message = "Subject: Oversized Email Message
  149. From: Max Mustermann <#{@sender_email_address}>
  150. To: shugo@example.com
  151. Message-ID: <#{@test_id}@zammad.test.com>
  152. Oversized Email Message Body #{'#' * 120_000}
  153. ".gsub(%r{\n}, "\r\n")
  154. imap.append(@folder, large_message, [], Time.zone.now)
  155. @channel.fetch(true)
  156. # 1. verify that the oversized email has been saved locally to:
  157. # /tmp/oversized_mail/yyyy-mm-ddThh:mm:ss-:md5.eml
  158. path = Rails.root.join('tmp/oversized_mail')
  159. target_files = Dir.entries(path).select do |filename|
  160. filename =~ %r{^.+?\.eml$}
  161. end
  162. assert_not(target_files.blank?, 'Large message .eml log file must be blank.')
  163. # 2. verify that a postmaster response email has been sent to the sender
  164. imap.select('inbox')
  165. message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
  166. assert_equal(message_ids.count, message_count, 'Must not have received a reply from the postmaster')
  167. # 3. check if original mail got removed
  168. imap.select(@folder)
  169. message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
  170. imap_message_id = message_ids.last
  171. msg = imap.fetch(imap_message_id, 'RFC822')[0].attr['RFC822']
  172. imap.store(imap_message_id, '+FLAGS', [:Deleted])
  173. imap.expunge
  174. assert(msg.present?, 'Oversized Email Message')
  175. assert_equal(message_ids.count, 1, 'Original customer mail must be deleted.')
  176. # final clean up
  177. imap.delete(@folder)
  178. @channel.destroy!
  179. end
  180. teardown do
  181. Setting.set('postmaster_max_size', 10)
  182. end
  183. end