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

@@ -5,7 +5,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '612041635d962d37f2f400ba1974bec5456ccd1e',
'reference' => '300a1aff9fdc5c7baa1ececd256c95f541a83a1a',
'name' => '__root__',
'dev' => true,
),
@@ -16,7 +16,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '612041635d962d37f2f400ba1974bec5456ccd1e',
'reference' => '300a1aff9fdc5c7baa1ececd256c95f541a83a1a',
'dev_requirement' => false,
),
'carbonphp/carbon-doctrine-types' => array(
@@ -28,6 +28,15 @@
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
'dev_requirement' => false,
),
'directorytree/imapengine' => array(
'pretty_version' => 'v1.24.1',
'version' => '1.24.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../directorytree/imapengine',
'aliases' => array(),
'reference' => 'd545ff3f6d2155ce7f9f612f0444579477d7bab8',
'dev_requirement' => false,
),
'doctrine/inflector' => array(
'pretty_version' => '2.1.0',
'version' => '2.1.0.0',
@@ -37,6 +46,33 @@
'reference' => '6d6c96277ea252fc1304627204c3d5e6e15faa3b',
'dev_requirement' => false,
),
'doctrine/lexer' => array(
'pretty_version' => '3.0.1',
'version' => '3.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/lexer',
'aliases' => array(),
'reference' => '31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd',
'dev_requirement' => false,
),
'egulias/email-validator' => array(
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../egulias/email-validator',
'aliases' => array(),
'reference' => 'd42c8731f0624ad6bdc8d3e5e9a4524f68801cfa',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '2.11.0',
'version' => '2.11.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => 'bbb5e61349fa5cb822b3e87842b951088b76b81f',
'dev_requirement' => false,
),
'illuminate/collections' => array(
'pretty_version' => 'v12.28.1',
'version' => '12.28.1.0',
@@ -91,6 +127,15 @@
'reference' => '487bbe527806615b818e87c364d93ba91f27db9b',
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
'pretty_version' => 'v2.0.13',
'version' => '2.0.13.0',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
'reference' => 'b566ee0dd251f3c4078bed003a7ce015f5ea6dce',
'dev_requirement' => false,
),
'nesbot/carbon' => array(
'pretty_version' => '3.10.3',
'version' => '3.10.3.0',
@@ -100,6 +145,24 @@
'reference' => '8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f',
'dev_requirement' => false,
),
'php-di/invoker' => array(
'pretty_version' => '2.3.7',
'version' => '2.3.7.0',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/invoker',
'aliases' => array(),
'reference' => '3c1ddfdef181431fbc4be83378f6d036d59e81e1',
'dev_requirement' => false,
),
'php-di/php-di' => array(
'pretty_version' => '7.1.1',
'version' => '7.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/php-di',
'aliases' => array(),
'reference' => 'f88054cc052e40dbe7b383c8817c19442d480352',
'dev_requirement' => false,
),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
@@ -124,6 +187,51 @@
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'dev_requirement' => false,
),
'psr/container-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '^1.0',
),
),
'psr/http-factory' => array(
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/http-message' => array(
'pretty_version' => '2.0',
'version' => '2.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/log' => array(
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
'dev_requirement' => false,
),
'psr/simple-cache' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
@@ -133,6 +241,15 @@
'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
'dev_requirement' => false,
),
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'dev_requirement' => false,
),
'spatie/once' => array(
'dev_requirement' => false,
'replaced' => array(
@@ -166,6 +283,42 @@
'reference' => 'db488a62f98f7a81d5746f05eea63a74e55bb7c4',
'dev_requirement' => false,
),
'symfony/mime' => array(
'pretty_version' => 'v8.1.0',
'version' => '8.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/mime',
'aliases' => array(),
'reference' => 'b164ae7e3f7915aacfe9ee155f2f358502440664',
'dev_requirement' => false,
),
'symfony/polyfill-iconv' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
'aliases' => array(),
'reference' => '2c5729fd241b4b22f6e4b436bc3354a4f262df57',
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'reference' => 'dc21118016c039a66235cf93d96b435ffb282412',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.38.0',
'version' => '1.38.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '2d446c214bdbe5b71bde5011b060a05fece3ae6b',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.33.0',
'version' => '1.33.0.0',
@@ -175,6 +328,15 @@
'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
'dev_requirement' => false,
),
'symfony/polyfill-php83' => array(
'pretty_version' => 'v1.33.0',
'version' => '1.33.0.0',
@@ -244,5 +406,32 @@
'reference' => '6b8ef85d621bbbaf52741b00cca8e9237e2b2e05',
'dev_requirement' => false,
),
'zbateson/mail-mime-parser' => array(
'pretty_version' => '3.0.5',
'version' => '3.0.5.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mail-mime-parser',
'aliases' => array(),
'reference' => 'ff054c8e05310c445c2028c6128a4319cc9f6aa8',
'dev_requirement' => false,
),
'zbateson/mb-wrapper' => array(
'pretty_version' => '2.0.1',
'version' => '2.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mb-wrapper',
'aliases' => array(),
'reference' => '50a14c0c9537f978a61cde9fdc192a0267cc9cff',
'dev_requirement' => false,
),
'zbateson/stream-decorators' => array(
'pretty_version' => '2.1.1',
'version' => '2.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/stream-decorators',
'aliases' => array(),
'reference' => '32a2a62fb0f26313395c996ebd658d33c3f9c4e5',
'dev_requirement' => false,
),
),
);