Rewrite email parser using ImapEngine, harden processing loop

Replace webklex/php-imap with directorytree/imapengine in the ticket
email parser. ImapEngine is pure PHP over sockets.

Parser improvements:
- Wrap per-message processing in try/catch so one malformed email
  can't abort the run; failures are flagged and logged with UID
- Query unseen + unflagged so previously-failed (flagged) messages
  are no longer re-processed on every cron run
- Skip vacation/auto-responder emails (RFC 3834) to prevent mail
  loops with the ticket auto-reply
- Cap messages per run (50) and attachment size (15MB); inline
  images over 2MB are stored as attachments instead of base64-embedded
  in ticket details
- Atomic lock file creation
- preg_quote() the ticket prefix in subject matching
- Dedupe CC watchers and exclude the sender
- Map webklex 'tls' encryption setting to STARTTLS for compatibility

NDR/DSN parsing now walks MIME parts via the underlying
zbateson parser instead of relying on attachment extraction.
This commit is contained in:
johnnyq
2026-06-12 16:56:39 -04:00
parent 300a1aff9f
commit 2204bd52f4
701 changed files with 111718 additions and 940 deletions

View File

@@ -0,0 +1,127 @@
<?php
namespace DirectoryTree\ImapEngine;
use BackedEnum;
interface FlaggableInterface
{
/**
* Mark the message as read. Alias for markSeen.
*/
public function markRead(): void;
/**
* Mark the message as unread. Alias for unmarkSeen.
*/
public function markUnread(): void;
/**
* Mark the message as seen.
*/
public function markSeen(): void;
/**
* Unmark the seen flag.
*/
public function unmarkSeen(): void;
/**
* Mark the message as answered.
*/
public function markAnswered(): void;
/**
* Unmark the answered flag.
*/
public function unmarkAnswered(): void;
/**
* Mark the message as flagged.
*/
public function markFlagged(): void;
/**
* Unmark the flagged flag.
*/
public function unmarkFlagged(): void;
/**
* Mark the message as deleted.
*/
public function markDeleted(bool $expunge = false): void;
/**
* Unmark the deleted flag.
*/
public function unmarkDeleted(): void;
/**
* Mark the message as a draft.
*/
public function markDraft(): void;
/**
* Unmark the draft flag.
*/
public function unmarkDraft(): void;
/**
* Mark the message as recent.
*/
public function markRecent(): void;
/**
* Unmark the recent flag.
*/
public function unmarkRecent(): void;
/**
* Determine if the message is marked as seen.
*/
public function isSeen(): bool;
/**
* Determine if the message is marked as answered.
*/
public function isAnswered(): bool;
/**
* Determine if the message is flagged.
*/
public function isFlagged(): bool;
/**
* Determine if the message is marked as deleted.
*/
public function isDeleted(): bool;
/**
* Determine if the message is marked as a draft.
*/
public function isDraft(): bool;
/**
* Determine if the message is marked as recent.
*/
public function isRecent(): bool;
/**
* Get the message's flags.
*
* @return string[]
*/
public function flags(): array;
/**
* Determine if the message has the given flag.
*/
public function hasFlag(BackedEnum|string $flag): bool;
/**
* Add or remove a flag from the message.
*
* @param '+'|'-' $operation
*/
public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void;
}