Commit Graph
23 Commits
Author SHA1 Message Date
d1c482cb64 fix(email): strip null bytes from inbound mailbox messages (#14546)
Inbound emails containing null bytes could fail while Chatwoot persisted
the incoming message, causing IMAP sync to drop and repeatedly retry the
same malformed email. This strips null bytes at the inbound mailbox
message persistence boundary so the rest of the email can be saved
normally.

Fixes
https://linear.app/chatwoot/issue/CW-7165/argumenterror-string-contains-null-byte-argumenterror
Sentry: https://chatwoot-p3.sentry.io/issues/7405946944/

## Test
- [x] added specs
- [x] manually tested the logic on prod for the inbox(email) which was
throwing the error

## What changed

- Sanitizes null bytes only while building attributes for inbound
mailbox message persistence.
- Uses the sanitized source ID for both duplicate lookup and message
creation.
- Adds regression coverage for the mailbox message creation path.

---------

Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
Co-authored-by: Sony Mathew <sony@chatwoot.com>
2026-06-08 17:12:25 +05:30
4371793741 fix: captain-v2 cannot see image attachments shared via email (#14449)
## Description

Inbound email attachments are stored with `file_type: 'file'` regardless
of their actual MIME type. As a result, image screenshots shared by
customers via email are not exposed to Captain V2's multimodal pipeline
— `Captain::OpenAiMessageBuilderService#attachment_parts` selects images
via `attachments.where(file_type: :image)` and emits a placeholder
`"User has shared an attachment"` text part instead of an `image_url`
part. The model never gets the image, so Captain keeps asking the
customer to retype information that is already visible in the
screenshot.

This PR makes the email mailbox derive `file_type` from the blob's
`content_type` using the existing shared `FileTypeHelper`, matching how
every other inbound channel (`twilio`, `sms`, `telegram`, `line`,
`tiktok`, `twitter`, `messenger`) and `MessageBuilder` already classify
attachments.

Fixes #14448

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

Reproduced and verified on a self-hosted production instance:

1. Real customer reply via email with a PNG screenshot of an in-app
error.

   Before:
   ```ruby
   a = Message.find(<id>).attachments.first
   a.file_type                # => "file"
   a.file.blob.content_type   # => "image/png"
Captain::OpenAiMessageBuilderService.new(message:
a.message).generate_content
   # => [{type: 'text', text: '...'},
# {type: 'text', text: 'User has shared an attachment'}]  no image_url
   ```
Captain reply: "Please copy and paste the full error text…" (model never
saw the image).

2. After the patch + force-recreate, same conversation:
   ```ruby
   a.file_type                # => "image"
Captain::OpenAiMessageBuilderService.new(message:
a.message).generate_content
   # => [{type: 'text',      text: '...'},
# {type: 'image_url', image_url: {url: 'https://.../<blob>.png'}}] 
   ```
Captain reply now correctly references the on-screen error text from the
screenshot via the multimodal vision path — no more deflection.

3. Regression sanity-check on non-image attachments (PDF / Office docs):
`file_type` falls through to `:file`, behavior unchanged.

## Notes for self-hosted operators

Existing email image attachments in the DB will still have `file_type:
'file'`. A one-shot backfill is straightforward and safe (no data loss,
only metadata):

```ruby
Attachment.joins(message: :conversation)
          .where(messages: { content_type: 'incoming_email' })
          .where(file_type: 'file')
          .find_each do |a|
  next unless a.file.attached?
  ct = a.file.blob.content_type.to_s
  next unless ct.start_with?('image/', 'audio/', 'video/')
  new_type = ct.start_with?('image/') ? :image : (ct.start_with?('video/') ? :video : :audio)
  a.update_columns(file_type: Attachment.file_types[new_type])
end
```

## Checklist

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective — happy to add a
`mailbox_helper_spec` example for `process_regular_attachments` if
maintainers prefer; existing specs in that file focus on inline-image
handling.

---------

Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
2026-05-19 10:11:32 +05:30
42bba748cf fix(mailbox): render inline images without Content-Disposition (#11949)
## Description
This pull request addresses issue #11948, where inline images embedded
in emails (such as those sent from Outlook) are not rendered correctly
if the Content-Disposition header is missing.

The solution ensures that images referenced via cid: in the HTML body
are correctly identified and rewritten using url_for.

Fixes #11948

## Type of change
Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?
Added test: detects image inline attachment by cid reference when
Content-Disposition is missing

## Checklist:

- [X] My code follows the style guidelines of this project
- [X] I have performed a self-review of my code
- [X] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [X] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Sony Mathew <sony@chatwoot.com>
Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com>
2026-05-06 18:56:31 +05:30
PranavandGitHub 24f49b9b5a fix: Process non-image inline attachments as regular attachments (#10998)
In Chatwoot, we rely on the Content-ID for inline attachments to replace
the link with the uploaded attachment URL. Our expectation was that only
images would be inline, while other attachments would not. However,
email clients like Apple Mail (sigh) allow users to send inline
attachments that are not images, and these attachments often lack a
Content-ID. This creates significant issues in rendering.
 
I investigated how other email clients handle this scenario. When
viewing the same email (sent from Apple Mail) in Gmail, only one image
appears—and it’s treated as an attachment, not inline. This happens
because both attachments are the same image, and Apple Mail only sends
one copy. See the screenshot below.

| Apple Mail | Gmail | 
| -- | -- | 
| <img width="646" alt="Screenshot 2025-02-27 at 8 20 17 PM"
src="https://github.com/user-attachments/assets/e0d1cd2d-e47c-4081-a53b-7a67106341b3"
/> | <img width="360" alt="Screenshot 2025-02-27 at 8 20 51 PM"
src="https://github.com/user-attachments/assets/b206e56e-8f86-43e9-867b-d895c36aff78"
/> |

A good fix for this would be to check if the Content-ID is missing and
then upload the file as a regular attachment. However, the Mail gem (for
some reason) automatically adds a default Content-ID to inline parts. I
need to dig into the source code to understand why this happens.

For now, I’ve implemented a check to treat non-image attachments as
regular attachments. Inline image attachments are already handled by
appending an image tag at the end if the content-id is not found in the
body. A sample conversation to test this behavior is
[here](https://app.chatwoot.com/app/accounts/1/conversations/46732).
2025-02-28 13:33:48 -08:00
PranavandGitHub 8cdbdaaa07 fix: Process attachments as regular attachments if the text/plain or text/html part is empty (#10379)
Some email clients automatically set Content-Disposition to inline for
specific content types, such as images. In cases where the email body is
empty, inline attachments may not display correctly due to our previous
implementation. Our assumption was that these attachments are referenced
within text/plain or text/html parts.

Customer-reported issues, especially with Apple Mail, show emails with
attachments marked as inline but without any corresponding text parts.
This leads to missing attachments even though would have processed the
attachment.

This update introduces a check for the presence of a text part. If none
exists, inline attachments are treated as regular attachments and added
to the external attachments array, ensuring that all attachments display
properly.

<details>
<summary><b>Script to update the existing emails that are already
available in the system</b></summary>

```rb
def update_content id
  message = Message.find id
  conversation = message.conversation
  message_id = message.source_id

  channel = message.inbox.channel

  authentication_type = 'XOAUTH2'
  imap_password = Google::RefreshOauthTokenService.new(channel: channel).access_token
  imap = Net::IMAP.new(channel.imap_address, port: channel.imap_port, ssl: true)
  imap.authenticate(authentication_type, channel.imap_login, imap_password)
  imap.select('INBOX')

  results = imap.search(['HEADER', 'MESSAGE-ID', message_id])
  message_content = imap.fetch(results.first, 'RFC822').first.attr['RFC822']
  mail = MailPresenter.new(Mail.read_from_string(message_content))

  mail_content = if mail.text_content.present?
                   mail.text_content[:reply]
                 elsif mail.html_content.present?
                   mail.html_content[:reply]
                 end

  attachments = mail.attachments.last(Message::NUMBER_OF_PERMITTED_ATTACHMENTS)
  inline_attachments = attachments.select { |attachment| attachment[:original].inline? && mail_content.present? }
  regular_attachments = attachments - inline_attachments

  regular_attachments.each do |mail_attachment|
    attachment = message.attachments.new(
      account_id: conversation.account_id,
      file_type: 'file'
    )
    attachment.file.attach(mail_attachment[:blob])
  end

  message.save!
end
```
</details>
2024-11-04 10:25:01 +01:00
Sojan JoseandGitHub 7968e98529 chore: Stop processing auto-response emails (#9606)
Stop processing auto-response emails
https://www.notion.so/chatwoot/Avoid-Auto-Replies-sorcerer-s-apprentice-mode-55ffb09efbd7451994f1ff852de4c168?pvs=4
2024-06-13 14:19:11 -07:00
Shivam MishraandGitHub c62c512ec4 fix: inline attachments not handled if tag was missing (#9068) 2024-03-06 17:15:29 +05:30
Shivam MishraandGitHub 33a6ad9f7e chore: add more logging to mailbox helpers [CW-3071] (#8909)
* chore: add more logging to mailbox helpers

* fix: deleted entries

* fix: log order

* refactor: log using `processed_mail.message_id`
2024-02-13 08:51:50 +05:30
Shivam MishraandGitHub e529e1206e fix: Update inline image processing logic to fix missing images when multiple inline images present (#7861) 2023-09-06 14:35:19 +05:30
Tejaswini ChileandGitHub f64f2138db fix: Update mail check for html_part (#7273) 2023-06-09 16:08:40 +05:30
Tejaswini ChileandGitHub aae6081d73 fix: check content disposition, for inline messages in mail (#7231) 2023-06-05 20:28:32 +05:30
Tejaswini ChileandGitHub 354010a6e1 chore: fetch mails with multiple attachments (#7030) 2023-05-14 10:02:36 +05:30
Sojan JoseandGitHub e310230f62 chore: Refactor Contact Inbox Builders (#5617)
- Remove duplicate code and move everything to builders
- fixes: #4680
2022-10-13 15:12:04 -07:00
Jordan BroughandGitHub 59b31615ed chore: Use "create!" and "save!" bang methods when not checking the result (#5358)
* Use "create!" when not checking for errors on the result
* Use "save!" when not checking the result
2022-09-13 17:40:06 +05:30
Aswin Dev P.SandGitHub 19c637eb33 chore: Fix sentry issues (#4940)
Fixes: #4810
2022-07-08 15:09:06 +05:30
Aswin Dev P.SandGitHub 9015d83679 chore: Fix sentry issues (#4863)
Fix sentry issues.

Fixes #4815, #4814, #4811, #4809
2022-06-15 16:20:19 +05:30
Tejaswini ChileandGitHub 57359be37e Fix: Find mailbox with cc email (#4372) 2022-04-08 11:20:19 +05:30
Sojan JoseandGitHub 467f3b9191 chore: Disable fetching new emails after mailbox error (#4176)
- Disabled email fetch job if credentials for the channel isn't working
- notify customers when the email channel isn't working

fixes: https://github.com/chatwoot/chatwoot/issues/4174
2022-03-22 12:14:17 +05:30
Tejaswini ChileandGitHub 1467a8fa33 Fix: parse verification mail (#3864)
Email parsing logic was stripping of HTML tables which was causing the issue in this case.

Fixes: #3731
2022-01-27 15:45:26 -08:00
Aswin Dev P.SandGitHub 24e6a92297 feat: IMAP Email Channel (#3298)
This change allows the user to configure both IMAP and SMTP for an email inbox. IMAP enables the user to see emails in Chatwoot. And user can use SMTP to reply to an email conversation.

Users can use the default settings to send and receive emails for email inboxes if both IMAP and SMTP are disabled.

Fixes #2520
2021-11-19 11:52:27 +05:30
Sojan JoseandGitHub acb06e7df6 chore: Prevent notification email loop (#3386)
Configuring an agent email also as a support email inbox leads to conversations getting created in a loop if notifications were also configured to the same email.
2021-11-15 19:15:51 +05:30
6998e9aa2d fix: Update email message_id parsing order (#3073)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-10-12 20:46:00 +05:30
Sony MathewandGitHub f9b0427751 feat: email channel backend (#140) (#1255)
* feat: added support mailbox to handle email channel (#140)

Added a new mailbox called 'SupportMailbox' to handle all the
incoming emails other than reply emails.

An email channel will have a support email and forward email
associated with it. So we filter for the right email inbox based on
the support email of that inbox and route this to this mailbox.

This mailbox finds the account, inbox, contact (create a new one
if it does not exist) and creates a conversation and adds the
email content as the first message in the conversation.

Other minor things handled in this commit:

* renamed the procs for routing emails in application mailbox
* renamed ConversationMailbox to ReplyMailbox
* Added a fallback content in MailPresenter
* Added a record saving (bang) versions of enabling and disabling
features in Featurable module
* added new factory for the email channel

refs: #140
2020-09-21 22:44:22 +05:30