Emptying Deleted Items back-referenced one Email/query straight into one
Email/set, so every id in the folder arrived in a single call. Stalwart
refuses the whole call over maxObjectsInSet with requestTooLarge, which
left a folder of 5192 messages impossible to empty at all.
Walk the folder a page at a time instead: the filter re-runs each pass,
so the next page is whatever is still there. A pass that destroys nothing
stops the loop and reports the server's error rather than spinning.
The same defect sat in two neighbours. Delete-forever on a large
selection sent every id in one Email/set, and mark-all-read fed up to
5000 ids into an Email/get that only echoed them back - past
maxObjectsInGet, and for no gain, since the query already returned them.
Both now page through the same ceiling, read from the session rather than
hardcoded.
The mock advertised maxObjectsInSet but never enforced it, so none of
this could fail in a test. It now rejects oversized get and set calls the
way Stalwart does.
While here, restrict emptying to Deleted Items. It was offered on Junk
too, where a permanent one-shot clear is harder to justify; Junk is now
select-all plus Delete, which takes the batched path.
Every message ihasmail sent set cc, bcc and replyTo to null when unused, and
inReplyTo/references likewise on a new message. Stalwart parses those
properties with try_into_address_list, which returns None for null, and the
create is rejected outright:
if let Some(addresses) = value.try_into_address_list() { ... }
else { response.invalid_property_create(id, header); continue 'create; }
So every send failed with "Invalid property or value.", new messages and
replies alike, regardless of attachments or signature. The mock server
accepts anything, which is why this only showed up against a real server.
Empty header properties are now omitted. On a create there is no previous
value to clear, so null was never needed - only the properties actually being
set belong in the object.
buildEmailObject is exported so the shape can be tested directly, with a
regression test that no property is ever null.