Skip to content

Fix deleting all folder messages on 'select all' when the search context is lost#10255

Open
TowyTowy wants to merge 1 commit into
roundcube:masterfrom
TowyTowy:fix/9671-delete-lost-search-set
Open

Fix deleting all folder messages on 'select all' when the search context is lost#10255
TowyTowy wants to merge 1 commit into
roundcube:masterfrom
TowyTowy:fix/9671-delete-lost-search-set

Conversation

@TowyTowy

@TowyTowy TowyTowy commented Jul 9, 2026

Copy link
Copy Markdown

Closes #9671.

Problem

When a search result is deleted using "select all", the client sends _uid=* together with _search=<id>. The delete handler resolves * against the storage search set. If the search context is no longer in the session — e.g. it was overwritten by a new search in another browser tab — rcmail_action_mail_index::run() does not call set_search_set(), so the storage object has no search set. rcube_storage::parse_uids('*') then falls back to 1:* and every message in the folder is deleted instead of only the (now lost) search result.

This matches the analysis in #9671:

I can see how one tab removed a "search state" from the session, and the other tab, when not seeing the data in session, falls back to deleting all messages in a folder. I think the solution will be to bail out if _search is set in the request, but the "search state" does not exist in session.

Fix

Bail out with an error in the delete handler when a _uid=* request is bound to a search (_search present) but no search set exists on the storage object. Single-message and explicit-UID deletes are unaffected, and a normal "select all" in a non-search folder view (no _search) still works.

Tests

Added a regression test in tests/Actions/Mail/DeleteTest.php that mocks a missing search set and asserts the action aborts without calling delete_message(). It fails on current master (the un-guarded code calls delete_message('*'), i.e. the whole-folder delete) and passes with this change. CHANGELOG entry included.

To reproduce manually: open a search result with more than one message; in a second tab run a different search (overwriting the session search state); back in the first tab, Select all → Delete. Without this change the whole folder is emptied.


Disclosure: this fix was prepared with the assistance of an AI coding agent (Claude) and reviewed before submission.

@alecpl alecpl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same issue in delete, move and mark actions too (maybe more). So, I think we should move the check to index.php and bail out whenever search is requested but does not exist, and _action is not empty (or if it is an ajax action, I'm not sure which would be better).

Comment thread program/actions/mail/delete.php Outdated
// has been overwritten by a search in another browser tab). Otherwise
// the '*' would resolve to the whole folder and delete all its messages
// instead of only the search result (#9671).
if ($search_request && $_POST['_uid'] === '*' && !$rcmail->storage->get_search_set()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second condition can be removed. It does not matter what _uid is.

…le folder (roundcube#9671)

When the client sends a _search request parameter but the referenced
search context does not exist in the session anymore (e.g. it has been
overwritten by a search in another browser tab), mail actions fall back
to operating on the whole folder. E.g. deleting a search result with
"select all" (_uid=*) resolves '*' against the folder instead of the
search set and deletes every message in the folder.

Add a central guard in rcmail_action_mail_index::run(), which runs
before every mail action, that bails out with an error whenever a search
context is requested but not available and _action is not empty. This
covers delete, move, mark and all other actions operating on a search
result. Actions where _search is not a message-search reference
(autocomplete, list-contacts, search-contacts), periodic check-recent/
refresh requests that already degrade gracefully, and compose are
exempt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TowyTowy

Copy link
Copy Markdown
Author

Moved the check into rcmail_action_mail_index::run() so it now covers delete, move, mark and everything else dispatched through the mail task, and dropped the _uid condition plus the delete.php check entirely (it went there rather than top-level index.php because _search means something different in the contacts task). It bails when _search doesn't match the session context and _action is non-empty, with exemptions where _search isn't a message-search token (autocomplete, contact search), check-recent/refresh which already handle a lost search gracefully, and compose. If you'd prefer limiting it to ajax requests instead, that's a one-line change - happy to adjust.

@TowyTowy
TowyTowy force-pushed the fix/9671-delete-lost-search-set branch from 6585a58 to 3592f84 Compare July 18, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Selection all deleting not selected emails

2 participants