mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-03 20:48:07 +00:00 
			
		
		
		
	fix: Use BODY.PEEK[HEADER] to avoid parsing issues with mail providers (#8833)
Co-authored-by: Sojan <sojan@pepalo.com>
This commit is contained in:
		@@ -51,6 +51,11 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return if email_already_present?(channel, message_id)
 | 
					    return if email_already_present?(channel, message_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if message_id.blank?
 | 
				
			||||||
 | 
					      Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Empty message id for #{channel.email} with seq no. <#{seq_no}>."
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Fetch the original mail content using the sequence no
 | 
					    # Fetch the original mail content using the sequence no
 | 
				
			||||||
    mail_str = imap_client.fetch(seq_no, 'RFC822')[0].attr['RFC822']
 | 
					    mail_str = imap_client.fetch(seq_no, 'RFC822')[0].attr['RFC822']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,7 +79,7 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
 | 
				
			|||||||
    message_ids_with_seq = []
 | 
					    message_ids_with_seq = []
 | 
				
			||||||
    seq_nums.each_slice(10).each do |batch|
 | 
					    seq_nums.each_slice(10).each do |batch|
 | 
				
			||||||
      # Fetch only message-id only without mail body or contents.
 | 
					      # Fetch only message-id only without mail body or contents.
 | 
				
			||||||
      batch_message_ids = imap_client.fetch(batch, 'BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)]')
 | 
					      batch_message_ids = imap_client.fetch(batch, 'BODY.PEEK[HEADER]')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # .fetch returns an array of Net::IMAP::FetchData or nil
 | 
					      # .fetch returns an array of Net::IMAP::FetchData or nil
 | 
				
			||||||
      # (instead of an empty array) if there is no matching message.
 | 
					      # (instead of an empty array) if there is no matching message.
 | 
				
			||||||
@@ -85,7 +90,7 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      batch_message_ids.each do |data|
 | 
					      batch_message_ids.each do |data|
 | 
				
			||||||
        message_id = build_mail_from_string(data.attr['BODY[HEADER.FIELDS (MESSAGE-ID)]']).message_id
 | 
					        message_id = build_mail_from_string(data.attr['BODY[HEADER]']).message_id
 | 
				
			||||||
        message_ids_with_seq.push([data.seqno, message_id])
 | 
					        message_ids_with_seq.push([data.seqno, message_id])
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -97,7 +102,7 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
 | 
				
			|||||||
  # created between yesterday and today and returns message sequence numbers.
 | 
					  # created between yesterday and today and returns message sequence numbers.
 | 
				
			||||||
  # Return <message set>
 | 
					  # Return <message set>
 | 
				
			||||||
  def fetch_available_mail_sequence_numbers(imap_client)
 | 
					  def fetch_available_mail_sequence_numbers(imap_client)
 | 
				
			||||||
    imap_client.search(['BEFORE', tomorrow, 'SINCE', yesterday])
 | 
					    imap_client.search(['SINCE', yesterday])
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def fetch_mail_for_ms_provider(channel)
 | 
					  def fetch_mail_for_ms_provider(channel)
 | 
				
			||||||
@@ -161,8 +166,4 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
 | 
				
			|||||||
  def yesterday
 | 
					  def yesterday
 | 
				
			||||||
    (Time.zone.today - 1).strftime('%d-%b-%Y')
 | 
					    (Time.zone.today - 1).strftime('%d-%b-%Y')
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					 | 
				
			||||||
  def tomorrow
 | 
					 | 
				
			||||||
    (Time.zone.today + 1).strftime('%d-%b-%Y')
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								spec/fixtures/files/mail_with_no_date.eml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								spec/fixtures/files/mail_with_no_date.eml
									
									
									
									
										vendored
									
									
								
							@@ -2,5 +2,6 @@ Date:
 | 
				
			|||||||
From: testemail@gmail.com
 | 
					From: testemail@gmail.com
 | 
				
			||||||
To: imap@outlook.com
 | 
					To: imap@outlook.com
 | 
				
			||||||
Subject: test text only mail
 | 
					Subject: test text only mail
 | 
				
			||||||
 | 
					Message-Id: <0CB459E0-0336-41DA-BC88-E6E28C697DDB@chatwoot.com>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
text only mail
 | 
					text only mail
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,6 @@ RSpec.describe Inboxes::FetchImapEmailsJob do
 | 
				
			|||||||
  let(:ms_email_inbox) { create(:inbox, channel: microsoft_imap_email_channel, account: account) }
 | 
					  let(:ms_email_inbox) { create(:inbox, channel: microsoft_imap_email_channel, account: account) }
 | 
				
			||||||
  let!(:conversation) { create(:conversation, inbox: imap_email_channel.inbox, account: account) }
 | 
					  let!(:conversation) { create(:conversation, inbox: imap_email_channel.inbox, account: account) }
 | 
				
			||||||
  let(:inbound_mail) { create_inbound_email_from_mail(from: 'testemail@gmail.com', to: 'imap@outlook.com', subject: 'Hello!') }
 | 
					  let(:inbound_mail) { create_inbound_email_from_mail(from: 'testemail@gmail.com', to: 'imap@outlook.com', subject: 'Hello!') }
 | 
				
			||||||
  let(:inbound_mail_with_no_date) { create_inbound_email_from_fixture('mail_with_no_date.eml') }
 | 
					 | 
				
			||||||
  let(:inbound_mail_with_attachments) { create_inbound_email_from_fixture('multiple_attachments.eml') }
 | 
					  let(:inbound_mail_with_attachments) { create_inbound_email_from_fixture('multiple_attachments.eml') }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it 'enqueues the job' do
 | 
					  it 'enqueues the job' do
 | 
				
			||||||
@@ -53,25 +52,19 @@ RSpec.describe Inboxes::FetchImapEmailsJob do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it 'process the email with no date' do
 | 
					    it 'process the email with no date' do
 | 
				
			||||||
      email = Mail.new do
 | 
					      fixture_path = Rails.root.join('spec/fixtures/files/mail_with_no_date.eml')
 | 
				
			||||||
        to 'test@outlook.com'
 | 
					      eml_content = File.read(fixture_path)
 | 
				
			||||||
        from 'test@gmail.com'
 | 
					      inbound_mail_with_no_date = create_inbound_email_from_fixture('mail_with_no_date.eml')
 | 
				
			||||||
        subject :test.to_s
 | 
					      email_header = Net::IMAP::FetchData.new(1, 'BODY[HEADER]' => eml_content)
 | 
				
			||||||
        body 'hello'
 | 
					      imap_fetch_mail = Net::IMAP::FetchData.new(1, 'RFC822' => eml_content)
 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      imap_fetch_mail = Net::IMAP::FetchData.new
 | 
					 | 
				
			||||||
      imap_fetch_mail.attr = { seqno: 1, RFC822: email }.with_indifferent_access
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      imap = double
 | 
					      imap = double
 | 
				
			||||||
 | 
					 | 
				
			||||||
      allow(Net::IMAP).to receive(:new).and_return(imap)
 | 
					      allow(Net::IMAP).to receive(:new).and_return(imap)
 | 
				
			||||||
      allow(imap).to receive(:authenticate)
 | 
					      allow(imap).to receive(:authenticate)
 | 
				
			||||||
      allow(imap).to receive(:select)
 | 
					      allow(imap).to receive(:select)
 | 
				
			||||||
      allow(imap).to receive(:search).and_return([1])
 | 
					      allow(imap).to receive(:search).and_return([1])
 | 
				
			||||||
      allow(imap).to receive(:fetch).and_return([imap_fetch_mail])
 | 
					      allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
 | 
				
			||||||
 | 
					      allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
 | 
				
			||||||
      allow(Mail).to receive(:read_from_string).and_return(inbound_mail_with_no_date.mail)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      imap_mailbox = double
 | 
					      imap_mailbox = double
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user