exmdb, zcore, php_mapi: retroactive rule execution - #282
Open
r4nc0r wants to merge 1 commit into
Open
Conversation
Add exmdb rules_execute (0x99) and zcore rulesexecute (0x5e), exposed as mapi_folder_rulesexecute(), to run a folder's rules over messages already in it. Reuses the Onestep engine rather than reimplementing action dispatch. Retroactive execution differs from delivery in three ways, all because the message pre-exists: the (folder, message) pair is verified, since folder_id only selects the rule set while every action addresses the message by id; mutated and deleted messages get a change number and notifications, which delivery omits because nothing has observed a new message yet; and action_flags gates which action classes may run. RX_ACT_SAFE (the default) permits only reversible actions. Hard delete and the sending actions -- reply, forward, bounce, delegate -- must be requested by name, since replaying them over an archive destroys mail or emits it, possibly years late and once per message. zcore caps the batch, deduplicates message ids so a repeated entryid cannot amplify one forward rule into many, and reports processed/skipped/failed without aborting the batch on a single bad entryid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a way to run a folder's rules over messages already in it, rather than only at delivery time: exmdb RPC
rules_execute(0x99), zcore RPCrulesexecute(0x5e), and the PHP bindingmapi_folder_rulesexecute().Why
This exists for the grommunio-web "Run rules now" feature (grommunio/grommunio-web#97, implementing grommunio/grommunio-web#86). Because gromox currently exposes no way to execute rules against an existing message, that PR had to reimplement a subset of the rule engine in PHP — roughly 368 lines in
class.rulesmodule.phpthat walk a folder, evaluate each rule's condition as a table restriction, and dispatch the actions by hand.That duplication is the problem this addresses. The PHP executor covers
OP_MOVE,OP_COPY,OP_DELETE,OP_MARK_AS_READandOP_TAG, and reports forward/reply/bounce/deferred/delegate/OOF as skipped, because it cannot reasonably implement them. gromox's own Onestep engine already implements all of them. Exposing the engine lets the web client's server side be reduced to a thin call, removes a second rule executor that has to stay in agreement with this one, and makes the remaining action types work rather than be skipped.Approach
message_rule_new_message()is reused as-is rather than reimplemented; the delivery entry point and the new one now share a singlerule_exec_common(), withexmdb_server::rule_new_message()a thin wrapper that behaves exactly as before.Retroactive execution differs from delivery in three ways, all of which follow from the message pre-existing rather than having just arrived.
The
(folder, message)pair is verified against the store.folder_idonly selects which rule set is loaded, while every action addresses the message by id alone — theOP_DELETEpath is a bareDELETE FROM messages WHERE message_id=, andcu_copy_message()takes the mid. Entryids are unauthenticated and carry the two GC values independently, so without this check a caller holding rights on one folder could run its rules against a message in another. The same query excludes soft-deleted and FAI messages, since neither is a legitimate target — FAI notably includes the extended-rule messages themselves.Messages the rules mutate or delete get a change number and notifications. Delivery deliberately omits both, because nothing has ever observed a newly-arrived message; that assumption does not hold here, and without this a retroactive move leaves the message visible in both folders on every connected client, and a retroactive mark-as-read never reaches other devices via ICS.
An action-class gate (
enum rule_action_flags) decides which classes may run at all. The default,RX_ACT_SAFE, permits only reversible actions.RX_ACT_DELETEandRX_ACT_SENDhave to be requested by name, because a rule'sOP_DELETEis a hard delete that does not pass through Deleted Items, and replaying reply/forward/bounce/delegate over an archive emits mail — possibly years late and once per matching message. Delivery is unaffected: it passesRX_ACT_ALL, and the gate defaults to permitting everything, so no existing call site changes behaviour.zcore layer
zs_rulesexecute()requiresfrightsOwneron the folder, takes an entryid batch rather than a whole folder, caps the batch, and deduplicates message ids so a repeated entryid cannot amplify a singleOP_FORWARDrule into many outbound mails. Per-message failures do not abort the batch — since the actions already applied cannot be rolled back — and the call returnsprocessed,skippedandfailedso the caller can report all three.The batch is the caller's unit of work by design: zcore holds the per-user session lock for a whole request, so a folder-wide variant would stall that user's other requests, including notification polling, for the duration.
Notes for review
Two points I would rather have a maintainer's opinion on than decide unilaterally.
A delegate holding
frightsOwneron a folder can author a forwarding rule and then trigger it in bulk over the whole folder. This is amplification of something already reachable viazs_modifyrulesplus inbound mail, not a new capability, but the retroactive call turns a slow drip into a single request, and it may warrant an audit log line or a tighter right.The batch cap is currently 1000. That is a large number for a loop that holds the session lock, particularly when
RX_ACT_SENDmakes each iteration a potential SMTP submission; a lower cap, or a separate lower cap whenRX_ACT_SENDis set, may be the better default.